给定一个长度为2的字节数组,我们有两种可能的shuffle。 01
和10
长度为3将允许这些随机播放选项012
,021
,102
,120
,102
,201
,{{ 1}}。总共2x3 = 6个选项。
长度为4将具有6x4 = 24。长度为5将有24x5 = 120个选项,等等。
因此,一旦您随机选择了其中一个随机播放选项,您如何存储它?您可以存储210
以指示如何对4个字节进行混洗。但这需要5x3 = 15位。我知道它可以用7位完成,因为只有120种可能性。
如何更有效地存储随机指令?它应该是一个可以缩放的算法。
修改:发布新内容之前,请先查看my own answer below。我确信这些已有的答案中有很多信息,但我对此并不了解。
答案 0 :(得分:4)
如果你有一组well-ordering元素正在改组,那么你可以为所有排列的集合创建一个良好的排序,并只存储一个整数,表示排列顺序中的哪个位置跌倒。
实施例:
改组1 4 5
:可能性是:
1 4 5 [0]
1 5 4 [1]
4 1 5 [2]
4 5 1 [3]
5 1 4 [4]
5 4 1 [5]
要存储排列415
,您只需存储2(零索引)。
如果对原始元素集有一个良好的排序,则可以通过迭代最左边元素的最小元素到最大元素来对排列集进行良好排序,同时迭代剩余元素对于右边的下一个地方,依此类推,直到你到达最右边的元素。您不需要存储此数组,您只需要能够以相同的顺序再次生成排列以“解包”存储的整数。
但是,尝试逐个生成所有排列将花费相当多的时间超出最小集合。您可以使用第一个(N-1)!
排列从第一个元素开始的观察,第二个(N-1)!
排列从第二个元素开始,然后对于以特定元素开头的每个排列,第一个{{1排列从第一个剩余元素开始,依此类推。这将允许你“填充”或“解包”(N-2)!
中的元素,除了实际生成阶乘的复杂性以及任意长度整数的除法和模数之外,这将有些实质性。
答案 1 :(得分:3)
你是正确的,只存储数据的排列,而不是数据本身,你只需要与ceil(log2(排列))一样多的位。对于N个项目,排列的数量是阶乘(N)或N!,因此您需要ceil(log2(factorial(N)))位来存储N个项目的排列而不存储项目。
无论你熟悉哪种语言,都应该有一个很好的方法来制作一个大的M位数组,用一个排列填充它,然后将它存储在一个存储设备上。
答案 2 :(得分:1)
常见的混洗算法是少数无偏的算法之一,是Fisher-Yates shuffle。算法的每次迭代都采用随机数,并根据该数字交换两个位置。通过存储这些随机数的列表,您可以稍后重现完全相同的排列。
此外,由于每个数字的有效范围是事先已知的,因此您可以将每个数字乘以较小数字的有效范围的乘积,将它们全部打包成一个大整数,就像一种基于变量的位置符号
答案 3 :(得分:1)
对于L项的数组,为什么不将订单打包成L * ceil(log2(L))位? (ceil(log2(L))是保存L个唯一值所需的位数。)例如,这里是“unshuffled”shuffle的表示,按顺序取项:
L=2: 0 1 (2 bits)
L=3: 00 01 10 (6 bits)
L=4: 00 01 10 11 (8 bits)
L=5: 000 001 010 011 100 (15 bits)
...
L=8: 000 001 010 011 100 101 110 111 (24 bits)
L=9: 0000 0001 0010 0011 0100 0101 0110 0111 1000 (36 bits)
...
L=16: 0000 0001 ... 1111 (64 bits)
L=128: 00000000 000000001 ... 11111111 (1024 bits)
与@ user470379的答案相比,这个方案的主要优点是提取索引非常容易,只需移位和掩码。无需重新生成排列表。对于大L来说这应该是一个很大的胜利:(对于128个项目,有128个!= 3.8562e + 215个可能的排列)。
(排列==“可能性”; factorial = L! = L * (L-1) * ... * 1
=完全按照计算可能性的方式)
此方法也不比存储排列索引大得多。您可以以1024位(32 x 32位整数)存储128项shuffle。它需要717位(23英寸)来存储128!
在更快的解码速度和不需要临时存储来实现排列的事实之间,存储额外的9个整数可能是非常值得的。
这是Ruby中的一个实现,应该适用于任意大小。 “shuffle指令”包含在数组instruction
中。第一部分使用@Theran提到的Fisher-Yates算法版本来计算shuffle
# Some Setup and utilities
sizeofInt = 32 # fix for your language/platform
N = 16
BitsPerIndex = Math.log2(N).ceil
IdsPerWord = sizeofInt/BitsPerIndex
# sets the n'th bitfield in array a to v
def setBitfield a,n,v
mask = (2**BitsPerIndex)-1
idx = n/IdsPerWord
shift = (n-idx*IdsPerWord)*BitsPerIndex
a[idx]&=~(mask<<shift)
a[idx]|=(v&mask)<<shift
end
# returns the n'th bitfield in array a
def getBitfield a,n
mask = (2**BitsPerIndex)-1
idx = n/IdsPerWord
shift = (n-idx*IdsPerWord)*BitsPerIndex
return (a[idx]>>shift)&mask
end
#create the shuffle instruction in linear time
nwords = (N.to_f/IdsPerWord).ceil # num words required to hold instruction
instruction = Array.new(nwords){0} # array initialized to 0
#the "inside-out" Fisher–Yates shuffle
for i in (1..N-1)
j = rand(i+1)
setBitfield(instruction,i,getBitfield(instruction,j))
setBitfield(instruction,j,i)
end
#Here is a way to visualize the shuffle order
#delete ".reverse.map{|s|s.to_i(2)}" to visualize the way it's really stored
p instruction.map{|v|v.to_s(2).rjust(BitsPerIndex*IdsPerWord,'0').scan(
Regexp.new('.'*BitsPerIndex)).reverse.map{|s|s.to_i(2)}}
以下是将shuffle应用于字符数组的示例:
A=(0...N).map{|v|('A'.ord+v).chr}
puts A*''
#Apply the shuffle to A in linear time
for i in (0...N)
print A[getBitfield(instruction,i)]
end
print "\n"
#example: for N=20, produces
> ABCDEFGHIJKLMNOPQRST
> MSNOLGRQCTHDEPIAJFKB
希望这不会太难转换为javascript或任何其他语言。
答案 4 :(得分:-3)
我很抱歉,如果以前的答案已经涵盖了这一点,但这是第一次,这些答案对我来说完全是陌生的。我可能已经提到我了解Java和JavaScript,而且我对 数学 一无所知......所以log2
,permutations
,{{1 }},factorial
对我来说都是未知的单词。
最重要的是,我最后(再次)使用StackOverflow作为白板来写出我的问题并在20分钟后回答了我的问题。我被非计算机生活所束缚,而且知道StackOverflow我认为为了节省超过20%的每个人容易浪费的时间已经太晚了。
无论如何,在所有三个现有答案中都迷失了方向。这是我所知道的答案
(用Javascript编写,但很容易将20行外国代码翻译成您选择的语言)
(请参阅此处的行动:http://jsfiddle.net/M3vHC)
编辑:感谢AShelly这次捕获:假设您的整数为32位,当密钥长度超过12时,这将失败(变得高度偏向)超过19,如果你的整数是64位)
well-ordering