我需要一种能够将32位有符号整数与另一个32位有符号整数进行一对一映射(即无冲突)的算法。
我真正关心的是足够的熵,因此函数的输出似乎是随机的。基本上我正在寻找类似于XOR Cipher的密码,但它可以生成更多任意外观的输出。虽然默默无闻,但安全并不是我真正关心的问题。
编辑以便澄清:
预期结果示例:
F(100)= 98456
F(101)= -758
F(102)= 10875498
F(103)= 986541
F(104)= 945451245
F(105)= -488554
就像MD5一样,改变一件事可能会改变很多事情。
我正在寻找一个数学函数,因此手动映射整数对我来说不是一个解决方案。对于那些提出要求的人来说,算法速度并不是很重要。
答案 0 :(得分:33)
使用任何32位分组密码!根据定义,块密码以可逆的方式将其范围内的每个可能输入值映射到唯一的输出值,并且通过设计,很难确定在没有密钥的情况下任何给定值将映射到什么值。如果安全或隐匿很重要,只需选择一个密钥,保密即可,并使用密码作为转换。
如果要将此提示扩展为非幂2范围,请参阅Secure Permutations with Block Ciphers上的帖子。
解决您的具体问题:
答案 1 :(得分:6)
我将尝试在一个更简单的示例中解释我的解决方案,然后可以轻松扩展到大型示例。
说我有一个4位数字。有16个不同的值。把它看作是一个四维立方体:4 dimensional cube http://www.ams.org/featurecolumn/images/january2009/klee8.jpg。
每个顶点代表其中一个数字,每个位代表一个维度。所以它的基本XYZW,其中每个维度只能有0或1的值。现在想象你使用不同顺序的维度。例如XZYW。每个顶点现在改变了它的数量!
您可以针对任意数量的维度执行此操作,只需置换这些维度即可。如果您不担心安全问题,这对您来说可能是一个很好的快速解决方案。另一方面,我不知道输出是否会“模糊”以满足您的需求,当然在完成大量映射后,映射可以反转(根据您的需要,这可能是优势或劣势。)
答案 2 :(得分:5)
以下文章为您提供4或5个映射示例,为您提供功能而不是构建映射集:www.cs.auckland.ac.nz/~john-rugis/pdf/BijectiveMapping.pdf
答案 3 :(得分:4)
除了生成随机查找表之外,您还可以使用以下函数组合:
答案 4 :(得分:3)
如果您的目标只是获得大致定义大小的数字的看似随机排列,那么还有另一种可能的方法:将数字集合减少为素数。
然后您可以使用表单
的映射f(i)=(i * a + b)%p
如果p确实是一个素数,这将是所有a = = 0和所有b的双射。对于较大的a和b,它看起来相当随机。
例如,在我偶然发现这个问题的情况下,我使用1073741789作为小于1的数字范围的素数<< 30.这让我只输了35个号码,这在我的情况下很好。
我的编码是
((n + 173741789) * 507371178) % 1073741789
,解码是
(n * 233233408 + 1073741789 - 173741789) % 1073741789
请注意,507371178 * 233233408%1073741789 == 1,因此这两个数字是模数字段1073741789的倒数(您可以使用扩展的欧几里德算法计算出这些字段中的倒数)。
我选择a和b相当随意,我只是确保它们大约是p的一半。
答案 5 :(得分:1)
您可以使用随机生成的查找表吗?只要表中的随机数是唯一的,您就会获得双射映射。但它并不是对称的。
对于所有32位值,一个16 GB的查找表可能不实用,但您可以为高字和低字使用两个单独的16位查找表。
PS:我认为你可以生成一个对称的双射查找表,如果这很重要的话。该算法将以空LUT开始:
+----+ +----+
| 1 | -> | |
+----+ +----+
| 2 | -> | |
+----+ +----+
| 3 | -> | |
+----+ +----+
| 4 | -> | |
+----+ +----+
选择第一个元素,为其分配随机映射。要使映射对称,也要指定反向:
+----+ +----+
| 1 | -> | 3 |
+----+ +----+
| 2 | -> | |
+----+ +----+
| 3 | -> | 1 |
+----+ +----+
| 4 | -> | |
+----+ +----+
选择下一个号码,再次分配随机映射,但选择尚未分配的号码。 (即在这种情况下,不要选择1或3)。重复直到LUT完成。这应该生成随机双射对称映射。
答案 6 :(得分:1)
取一个数字,乘以9,倒数,除以9。
123 <> 1107 <> 7011 <> 779
256 <> 2304 <> 4032 <> 448
1028 <> 9252 <> 2529 <> 281
应该模糊不清!!
编辑:它不是0结束整数的双射
900 <> 8100 <> 18 <> 2
2 <> 18 <> 81 <> 9
您始终可以添加以下特定规则: 取一个数字,除以10倍,乘以9,倒数,除以9,乘以10 ^ x。
所以
900 <> 9 <> 81 <> 18 <> 2 <> 200
200 <> 2 <> 18 <> 81 <> 9 <> 900
它有效!
编辑2:对于更多的障碍,您可以添加任意数字,并在结尾处减去。
900 < +256 > 1156 < *9 > 10404 < invert > 40401 < /9 > 4489 < -256 > 4233
123 < +256 > 379 < *9 > 3411 < invert > 1143 < /9 > 127 < -256 > -129
答案 7 :(得分:1)
这是我的简单想法: 你可以像PeterK提出的那样移动数字的位,但是你可以为每个数字设置不同的位排列,并且仍然可以解密它。
密码是这样的:
将输入数字视为位数I[0..31]
,输出为O[0..31]
。
准备64个随机生成的数组K[0..63]
。这将是你的关键。
从第一个随机数(I[K[0] mod 32]
)确定的位置获取输入数字位,并将其放在结果的开头(O[0]
)。现在决定在O[1]
放置哪个位,使用之前使用的位。如果它是0,使用K [1]生成I
中的位置,它是1,使用K [2](这意味着跳过一个随机数)。
现在这不会很好,因为你可能会采取相同的两次。为了避免它,在每次迭代后重新编号位,省略使用的位。要生成O[1]
使用I[K[p] mod 31]
的位置,其中p为1或2,具体取决于位O[0]
,因为还有31位,编号从0到30。
为了说明这一点,我举一个例子:
我们有一个4位数字和8个随机数:25,5,28,19,14,20,0,1.18。
I: 0111 O: ____
_
25 mod 4 = 1,所以我们将取位数为1的位(从0开始计算)
I: 0_11 O: 1___
_
我们刚刚取了一点值1,所以我们跳过一个随机数并使用28.剩下3位,所以计数位置我们取28 mod 3 = 1.我们取第一个(从0开始计数) )其余位:
I: 0__1 O: 11__
_
我们再次跳过一个数字,取14. 14 mod 2 = 0,所以我们取第0位:
I: ___1 O: 110_
_
现在没关系,但前一位是0,所以我们采取20. 20 mod 1 = 0:
I: ____ O: 1101
就是这样。
解读这样的数字很容易,只需要做同样的事情。从密钥中可以知道放置代码第一位的位置,下一个位置由先前插入的位确定。
这显然具有任何只会移动位的所有缺点(例如0变为0,MAXINT变为MAXINT),但似乎更难找到某人如何在不知道密钥的情况下加密数字,这必须保守秘密。
答案 8 :(得分:1)
如果您不想使用正确的加密算法(可能出于性能和复杂性的原因),您可以使用更简单的密码,如Vigenère cipher。这个密码实际上被描述为 lechiffreindéchiffrable(法语为'牢不可破的密码')。
这是一个简单的C#实现,它根据相应的键值移动值:
void Main()
{
var clearText = Enumerable.Range(0, 10);
var key = new[] { 10, 20, Int32.MaxValue };
var cipherText = Encode(clearText, key);
var clearText2 = Decode(cipherText, key);
}
IEnumerable<Int32> Encode(IEnumerable<Int32> clearText, IList<Int32> key) {
return clearText.Select((i, n) => unchecked(i + key[n%key.Count]));
}
IEnumerable<Int32> Decode(IEnumerable<Int32> cipherText, IList<Int32> key) {
return cipherText.Select((i, n) => unchecked(i - key[n%key.Count]));
}
当输入略有改变时,此算法不会在输出中产生大的偏移。但是,你可以使用另一种双射操作而不是添加来实现它。
答案 9 :(得分:0)
在一张大纸上画一个大圆圈。将所有从0到MAXINT的整数从圆顶顺时针方向写入。将所有整数从0逆时针写入MININT,再次等间距。观察到MININT位于圆圈底部的MAXINT旁边。现在在一张硬卡的两侧复制这个数字。通过两者的中心将硬卡固定在圆圈上。选择一个旋转角度,任何你喜欢的角度。现在你有一个1-1映射,它满足你的一些要求,但可能不够模糊。取消固定卡,将其翻转直径,任何直径。重复这些步骤(以任何顺序),直到你有一个你满意的双射。
如果您一直密切关注,使用您的首选语言对其进行编程并不困难。
评论后 澄清:如果您只是在纸张上旋转卡片,那么方法就像您抱怨一样简单。但是,当您翻转卡片时,对于任何(x+m) mod MAXINT
,映射不等于m
。例如,如果您使卡片未旋转并将其围绕直径翻转0(位于钟面的顶部),则1将映射到-1,2到-2,依此类推。 (x+m) mod MAXINT
仅对应于卡片的旋转。
答案 10 :(得分:0)
将数字分成两部分(16个最高有效位和16个最低有效位),并将两个16位结果中的位视为两个卡中的卡。将甲板混合成另一个甲板。
因此,如果您的初始号码为b31,b30,...,b1,b0
,那么您最终会得到b15,b31,b14,b30,...,b1,b17,b0,b16
。它实现快速而快速,反之亦然。
如果查看结果的十进制表示,系列看起来很模糊。
您可以手动映射0 - &gt; maxvalue和maxvalue - &gt; 0以避免它们映射到自身。