如何使用自定义字母生成GUID,其行为类似于MD5哈希在JavaScript中?

如何解决如何使用自定义字母生成GUID,其行为类似于MD5哈希在JavaScript中?

我想知道如何在给定输入字符串的情况下生成GUID,以使相同的输入字符串产生相同的GUID(有点像MD5哈希)。 MD5散列的问题在于它们仅保证低碰撞率,而不是唯一性。相反,我想要这样的东西:

guid('v1.0.0') == 1231231231123123123112312312311231231231
guid('v1.0.1') == 6154716581615471658161547165816154716581
guid('v1.0.2') == 1883939319188393931918839393191883939319

您将如何实现这种事情(最好在JavaScript中)?有可能做吗?我不确定从哪里开始。像uuid module之类的东西不需要种子字符串,并且它们不允许您使用自定义格式/字母。

我不是在寻找canonical UUID format,而是在寻找GUID,理想情况下,它只是由整数组成。

解决方法

您需要定义文本字符串(例如“ v1.0.0”)到40位长字符串(例如“ 123123 ...”)的一对一映射。这也称为 bijection ,尽管在​​您的情况下, injection (从输入到输出(不一定是到输入的简单一对一映射))可能就足够了。如您所述,散列函数不一定能确保这种映射,但是还有其他可能性,例如全周期linear congruential generators(如果它们采用种子,您可以一对一映射到输入字符串值)或其他可逆函数。

但是,如果可能的输入字符串的集合大于可能的输出字符串的集合,则由于{,您不能将所有输入字符串与所有输出字符串一一对应(不创建重复项)。 {3}}。

例如,除非您以某种方式限制了120个字符的格式,否则通常无法将所有120个字符的字符串与所有40位数字的字符串一一对应。但是,如果您可以接受将输入字符串限制为不超过10 40 值(大约132位)的方法,或者您可以通过其他方式利用冗余,则可以解决创建40位输出字符串的问题。输入字符串,以确保将它们无损压缩到40个十进制数字(约132位)或更小,这可能是不可能的。另请参见pigeonhole principle

该算法涉及两个步骤:

  • 首先,通过建立字符串的BigInt值,类似于另一个答案中给出的charCodeAt()方法,将字符串转换为stringToInt。如果任何charCodeAt()为0x80或更大,或者结果BigInt等于或大于BigInt(alphabet_length)**BigInt(output_length),则会引发错误。
  • 然后,通过获取BigInt的mod和输出字母的大小并将每个余数替换为输出字母中的相应字符,将整数转换为另一个字符串,直到BigInt达到0。
,

一种方法是使用该答案中的方法:

/*
 * uuid-timestamp (emitter)
 * UUID v4 based on timestamp
 *
 * Created by tarkh
 * tarkh.com (C) 2020
 * https://stackoverflow.com/a/63344366/1261825
 */
const uuidEmit = () => {
  // Get now time
  const n = Date.now();
  // Generate random
  const r = Math.random(); // <- swap this
  // Stringify now time and generate additional random number
  const s = String(n) + String(~~(r*9e4)+1e4);
  // Form UUID and return it
  return `${s.slice(0,8)}-${s.slice(8,12)}-4${s.slice(12,15)}-${[8,9,'a','b'][~~(r*3)]}${s.slice(15,18)}-${s.slice(s.length-12)}`;
};

// Generate 5 UUIDs
console.log(`${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}`);

只需将Math.random()调用换成另一个可以获取您的种子值的随机函数。 (目前有很多算法可以创建可播种的随机方法,因此我不会尝试开出一个特定的方法。)

大多数随机种子期望数字,因此您可以通过将字符值相加(将每个字符值乘以10 ^位置,从而始终得到一个唯一的数字)来将种子字符串转换为整数:

const stringToInt = str => 
  Array.prototype.slice.call(str).reduce((result,char,index) => result += char.charCodeAt(0) * (10**(str.length - index)),0);
  
console.log(stringToInt("v1.0.0"));
console.log(stringToInt("v1.0.1"));
console.log(stringToInt("v1.0.2"));


如果您想每次都生成相同的提取字符串,则可以采用与tarkh的uuidEmit()方法类似的方法,但要摆脱那些变化的位:

const strToInt = str => 
      Array.prototype.slice.call(str).reduce((result,0);

const strToId = (str,len = 40) => {
  // Generate random
  const r = strToInt(str);
  // Multiply the number by some things to get it to the right number of digits
  const rLen = `${r}`.length; // length of r as a string
  
  // If you want to avoid any chance of collision,you can't provide too long of a string
  // If a small chance of collision is okay,you can instead just truncate the string to
  //  your desired length
  if (rLen > len) throw new Error('String too long');
  
  // our string length is n * (r+m) + e = len,so we'll do some math to get n and m
  const mMax = 9; // maximum for the exponent,too much longer and it might be represented as an exponent. If you discover "e" showing up in your string,lower this value
  let m = Math.floor(Math.min(mMax,len / rLen)); // exponent
  let n = Math.floor(len / (m + rLen)); // number of times we repeat r and m
  let e = len - (n * (rLen + m)); // extra to pad us to the right length
    
  return (new Array(n)).fill(0).map((_,i) => String(r * (i * 10**m))).join('')
    + String(10**e);
};

console.log(strToId("v1.0.0"));
console.log(strToId("v1.0.1"));
console.log(strToId("v1.0.2"));
console.log(strToId("v1.0.0") === strToId("v1.0.0")); // check they are the same
console.log(strToId("v1.0.0") === strToId("v1.0.1")); // check they are different

请注意,这仅适用于较小的字符串(可能大约10个字符),但是它应该能够避免所有冲突。您可以对其进行调整以处理较大的字符串(从stringToInt中删除乘法位),但随后可能会发生冲突。

,

我建议使用MD5 ...

在经典的生日问题之后,在所有事物都相等的情况下,在23个人中,有2个人分享生日的几率是(参见https://en.wikipedia.org/wiki/Birthday_problem)...

enter image description here

为了估算MD5碰撞,我将简化生日问题公式,以帮助预测碰撞的可能性更高。

enter image description here

请注意,尽管在生日问题中,碰撞是肯定的结果,但在MD5问题中,碰撞是否定的结果,因此,提供比预期的更高的碰撞几率可以保守估计MD5碰撞的机会。再加上在某种程度上,这种较高的预测机会可以被认为是MD5输出中任何不均匀分布的忽悠因素,尽管我不相信没有上帝计算机也无法量化这一点...

MD5哈希长度为1​​6个字节,导致范围为256 ^ 16个可能的值。假设MD5算法的结果总体上是一致的,那么假设我们创建一个四千万(即一万亿或10 ^ 15)唯一的字符串来运行哈希算法。然后使用修改后的公式(以简化碰撞计算并添加保守的软糖因子),则碰撞的几率是...

enter image description here

因此,在10 ^ 15或一个四千万个唯一输入字符串之后,哈希冲突的估计赔率与赢得强力球或超级百万大奖的赔率相提并论(每{ {3}})。

还要注意256 ^ 16是340282366920938938463463374607431768211456,它是39位数字,落在40位数字的期望范围内。

因此,建议使用MD5哈希(转换为BigInt),如果确实发生冲突,我将很高兴为您找到一张彩票,只是有机会利用您的运气并拆分收益...

注意:我使用https://www.engineeringbigdata.com/odds-winning-powerball-grand-prize-r/进行计算。

,

虽然 UUID v4 仅用于生成随机 ID,但 UUID v5 更像是给定输入字符串和命名空间的散列。非常适合您的描述。

正如你已经提到的,你可以使用这个 npm 包:

npm install uuid

而且它很容易使用。

import {v5 as uuidv5} from 'uuid';

// use a UUIDV4 as a unique namespace for your application.
// you can generate one here: https://www.uuidgenerator.net/version4
const UUIDV5_NAMESPACE = '...';

// Finally,provide the input and namespace to get your unique id.
const uniqueId = uuidv5(input,namespace);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-