我想要一个5个字符的字符串,由从集合[a-zA-Z0-9]中随机选取的字符组成。

用JavaScript实现这一点的最佳方法是什么?


当前回答

我喜欢doubletap的Math.random().toString(36).substring(7)答案的简洁,但并不是因为它有很多像hack-likecrack正确指出的冲突。它生成了11个字符字符串,但在100万个样本中,重复率为11%。

这里有一个更长(但仍然很短)、更慢的替代方案,在100万个样本空间中只有133个副本。在极少数情况下,字符串仍将短于11个字符:

Math.abs(Math.random().toString().split('')
    .reduce(function(p,c){return (p<<5)-p+c})).toString(36).substr(0,11);

其他回答

这是我用过的。这是一对夫妇的组合。我在循环中使用它,它生成的每个ID都是唯一的。它可能不是5个字符,但它保证是唯一的。

var newId =
    "randomid_" +
    (Math.random() / +new Date()).toString(36).replace(/[^a-z]+/g, '');

回答“我需要随机字符串”问题(无论用什么语言)的问题是,实际上每个解决方案都使用有缺陷的字符串长度的主要规范。这些问题本身很少揭示为什么需要随机字符串,但我想挑战一下,你很少需要长度为8的随机字符串。您总是需要一些唯一的字符串,例如,用作某些目的的标识符。

有两种主要的方法可以获得严格唯一的字符串:确定性(这不是随机的)和存储/比较(这很麻烦)。我们该怎么做?我们放弃了幽灵。我们改为概率唯一性。也就是说,我们接受字符串不唯一的风险(无论多么小)。这就是理解碰撞概率和熵有帮助的地方。

因此,我将把不变的需求重新表述为需要一些字符串,但重复的风险很小。作为一个具体的例子,假设您希望生成500万个ID。您不希望存储和比较每个新字符串,并且希望它们是随机的,因此您接受一些重复的风险。例如,假设重复的风险小于一万亿分之一。那么你需要多长的绳子?嗯,这个问题没有具体说明,因为它取决于使用的字符。但更重要的是,这是错误的。您需要的是字符串熵的规范,而不是字符串的长度。熵可以与一些字符串中重复的概率直接相关。字符串长度不能。

这就是像EntropyString这样的库可以提供帮助的地方。要使用熵字符串在500万个字符串中生成重复概率小于1万亿的随机ID,请执行以下操作:

import {Random, Entropy} from 'entropy-string'

const random = new Random()
const bits = Entropy.bits(5e6, 1e12)

const string = random.string(bits)

“44hTNghjNHGGRHqH9”

熵字符串默认使用32个字符的字符集。还有其他预定义的字符集,您也可以指定自己的字符。例如,生成具有与上述相同熵但使用十六进制字符的ID:

import {Random, Entropy, charSet16} from './entropy-string'

const random = new Random(charSet16)
const bits = Entropy.bits(5e6, 1e12)

const string = random.string(bits)

“27b33372代码513715481f”

请注意,由于所使用的字符集中的字符总数不同,字符串长度不同。在指定数量的潜在字符串中重复的风险是相同的。字符串长度不是。最重要的是,重复的风险和字符串的潜在数量是明确的。不再猜测字符串长度。

从字符a-Za-z0-9中随机化字符串的另一种好方法:

function randomString(length) {
    if ( length <= 0 ) return "";
    var getChunk = function(){
        var i, //index iterator
            rand = Math.random()*10e16, //execute random once
            bin = rand.toString(2).substr(2,10), //random binary sequence
            lcase = (rand.toString(36)+"0000000000").substr(0,10), //lower case random string
            ucase = lcase.toUpperCase(), //upper case random string
            a = [lcase,ucase], //position them in an array in index 0 and 1
            str = ""; //the chunk string
        b = rand.toString(2).substr(2,10);
        for ( i=0; i<10; i++ )
            str += a[bin[i]][i]; //gets the next character, depends on the bit in the same position as the character - that way it will decide what case to put next
        return str;
    },
    str = ""; //the result string
    while ( str.length < length  )
        str += getChunk();
    str = str.substr(0,length);
    return str;
}

功能性方法。只有在应用程序的其他部分可以利用功能先决条件时,这个答案才实用。表演可能是垃圾,但写起来非常有趣。

//功能先决条件常量U=f=>f(f)常量Y=U(h=>f=>f(x=>h(h)(f)(x)))常量comp=f=>g=>x=>f(g(x))常量foldk=Y(h=>f=>Y=>([x,…xs])=>x===未定义?y:f(y)(x)(y=>h(f)(y))(xs))常量fold=f=>foldk(y=>x=>k=>k(f(y)(x)))常量映射=f=>fold(y=>x=>[…y,f(x)])([])constchar=x=>String.fromCharCode(x)常量concat=x=>y=>y.concat(x)常量concatMap=f=>comp(fold(concat)([]))(map(f))const irand=x=>数学地板(Math.random()*x)常量样本=xs=>xs[irand(xs.length)]//范围:从x到y的范围;[x…y]//编号->编号->[编号]常量范围=Y(f=>r=>x=>Y=>x>y?r:f([…r,x])(x+1)(y)) ([])//srand:从列表或ascii代码范围生成随机字符串//[(范围a)]->编号->[a]常量srand=comp(Y(f=>z=>rs=>x=>x===0?z:f(z+样本(rs))(rs)(x-1))([]))(concatMap(map(char)))//idGenerator:生成指定长度的标识符//数字->字符串常量idGenerator=srand([范围(48)(57),//包括0-9范围(65)(90),//包括A-Z范围(97)(122)//包括a-z])console.log(idGenerator(6))//=>TT688Xconsole.log(idGenerator(10))//=>SzaaUBlpI1console.log(idGenerator(20))//=>eYAaWhsfvLDhIBID1xRh

在我看来,如果不添加神奇的、做太多事情的功能,很难超越idGenerator的清晰度。

稍有改进

// ord : convert char to ascii code
// Char -> Number
const ord = x => x.charCodeAt(0)

// idGenerator : make an identifier of specified length
// Number -> String
const idGenerator = srand ([
  range (ord('0')) (ord('9')),
  range (ord('A')) (ord('Z')),
  range (ord('a')) (ord('z'))
])

玩得开心。让我知道你喜欢/学习什么^_^

例如,如果你想要一个随机的DNA序列,你可以循环遍历一个项目数组并递归地将它们添加到字符串变量中:

功能随机DNA(len){len=长度||100var nuc=新阵列(“A”、“T”、“C”、“G”)变量i=0变量n=0s=“”而(i<=len-1){n=数学楼层(Math.random()*4)s+=努克[n]我++}返回s}console.log(随机DNA(5));