哪个二元运算符或函数导致最少的冲突?

时间:2009-09-11 23:53:49

标签: math

假设我们有两个长点,x和y。涉及x和y的运算符或函数会产生另一个long,z,它最不可能等于将相同的运算符或函数应用于不同的x和y的结果?

Ex:增加将是一个糟糕的选择。 1 + 4 = 5,但2 + 3也等于5.

编辑:让我解释为什么我问这个问题。我正在制作一个太空RPG游戏。游戏的环境(solarsystems)将从两个种子程序生成。这些种子由宇宙中系统的x和y坐标组成。所以玩家在冒险过程中很可能会遇到(500,501)和(501,500)系统。我需要一种方法来使这些太阳系产生独特。但是,我想确保尽可能多的坐标对会产生独特的种子。

编辑2:我测试了给我的两个解决方案。 Accipitridae的答案远远优于Artelius的答案。以下是测试解决方案的代码:

HashSet<Long> set = new HashSet<Long>();

 for(int x=0; x<1000; x++)
  for(int y=0; y<1000; y++)
   //I commented out one of the solutions at a time
   set.add((long)(((x&0x7FFFFFFF) << 33) | ((y&0x7FFFFFFF) << 2) |   ((x>>>62) & 2) | (y>>>63)));//Artelius
   set.add((long)(x - y * 7046029254386353131l));//Accipitridae

 System.out.println(set.size());

根据HashSet的大小,我可以知道通过每种方法生成了多少个唯一种子。对于这些参数,Artelius的解决方案产生了2048个独特的长度,而Accipitridae产生了1000000个,这意味着根本没有碰撞。

感谢大家努力解决这个问题。 :)

3 个答案:

答案 0 :(得分:7)

如果(x1, y1)(x2, y2)是两对随机输入,请让f1 = f(x1,y1)f2 = f(x2,y2)

您要做的是最小化

P( f(x1,y1) = f(x2,y2) )
 = P(f1 = f2)
 = sum for i in [LONG_MIN ... LONG_MAX]
        of P(f1 = i) * P(f2 = i)
 = sum for i in [LONG_MIN ... LONG_MAX]
        of P(f1 = i)^2

因此,您希望最小化每个函数输出概率的平方和。由于概率的 sum 必须为1,我们知道:

sum for i in [LONG_MIN ... LONG_MAX]
     of P(f1 = i)
  = 1

我们也知道,对于所有i,P(f1 = i)介于0和1(含)之间。直观地说,最小化P(f1 = f2)是使f1的概率分布尽可能均匀的问题。 (这可以通过数学证明,但对于这个问题并不重要。)理想情况下,P(f1 = i)P(f1 = j)对于所有 long s i应该是相同的。 j

现在让我们看一下x和y性质的一些不同可能性。

首先是一般情况,其中x和y均匀分布在 long 的范围内。 (换句话说,x同样可能是很长的任何东西。所以是y。)在这种情况下,我们可以让f(x, y) = x+yf(x,y) = x-yf(x,y) = x XOR y,或者偶数f(x,y) = x和(假设正常的整数溢出)我们发现我们也有一个均匀分布的f,这意味着这些函数都是“最优的”。

但是f(x,y) = x示例告诉你,你在这里获得的确不是那么多。

但是,在实践中,您的x和y可能均匀分布。例如,如果x和y都是从范围[0,9999]中随机绘制的,那么使用f(x,y) = x + y * 10000总是为不同的输入产生不同的输出。

如果在每个(x,y)对中,x和y很可能彼此靠近,例如(1240,1249),(1,3),( - 159720,-159721),那么{ {1}}实际上是一个很好的候选函数。

如果x和y“可能不是很大”,那么你应该将x的16个低位与y的16个低位组合,即f(x,y) = x,因为较低的位将比高位。

如果x和y从不为负数,则效果非常好。但如果它们是,则符号位(表示该数字是正数还是负数)可能比16个低位中的一些更均匀地分布。所以你可能想要使用它。 E.g。

f(x,y) = ((x&0xFFFF) << 16) | (y&0xFFFF)

由于“可能不是很大”的情况很常见,我认为这个功能实际上通常会很好用。

答案 1 :(得分:7)

我喜欢Artelius的答案和分析。特别是使用

的提案
  

f(x,y)= x + y * K

对于某些常数K很有意思,我想补充一些想法。 我在这里做的不是新的,而是与之密切相关的 Fibonacci hashing,我认为是Knuth提出的。

如果我们使用的是64位整数,则碰撞f(x1,y1)= f(x2,y2)意味着

  

0 =(dx + dy * K)mod 2 64

其中dx = x1-x2且dy = y1-y2。这与

相同
  

K = -dx * dy -1 mod 2 64

其中dy -1 是模2的反模2 64 。如果我们想选择K使得f(x1,y1)!= f(x2,y2),只要差异dx和dy都很小,那么我们必须选择K这样

  

K = -dx * dy -1 mod 2 64

没有解决方案,dx和dy都很小。这可以通过例如选择来实现 K接近phi * 2 64 ,其中phi =(sqrt(5)-1)/ 2是黄金比例。黄金比率具有非常特殊的连续分数扩展,即在某种意义上,它是一个难以近似得分的数字。

因此,对于64位无符号整数,可以使用以下函数

  

f(x,y)= x + y * 11400714819323198485;

或等效使用带符号的64位整数

  

f(x,y)= x - y * 7046029254386353131;

答案 2 :(得分:-1)

只要将可能的输出值集合限制为SAME值集合作为您用作操作数的操作数,那么除了添加之外,您不能做任何更好的事情。实际上,增加可能是最好的选择,因为它是最简单的。 (见下面的分析)

有2 ^ 64个可能的长度,因此有2 ^ 127个可能的无序长对,并且只有2 ^ 64个可能长的答案,所以最佳可能的分配比率是有2 ^ 63个不同的对给出相同的答案,实际上,添加(有翻转)

编辑:根据以下评论。

然而,很多(比如它的N位)很长,有2 ^ N个不同的长度,所以有2 ^ N x 2 ^ N个有序长对,但是为了这个分析的目的,使用两个长x ,y与使用y和x完全相同(假设二元op是共同的),因此有2 ^(2N-1)个无序的long对。

所以使用无序对(一半),有2 ^ N x(2 ^ N-1)或2 ^(2N-1)对长没有重复。 (如果N = 64,则为2 ^ 127)因此,答案(从较小的2 ^ 64个长集合)到无序的操作数对(较大的2 ^ 127对集合)的最大“分布”是它们是平均分配的。这就是增加会做什么,因为对于所有长期集中的每一个可能的长,它与其他所有长的总和(与翻转)将是一组......每一个长。

使用有序对的唯一做法是允许你使用非交换操作数,但是你必须处理答案不在你用于操作数的集合中的所有情况(如5 / 4)但即使你只是假设四舍五入,对分析的唯一影响是通过使用有序对,你得到2 ^ 2N个不同的操作数对,而不是2 ^(2N-1)。

您可能要做的是将用作操作数的整数集限制为小于可能长数的平方根(因此,如果使用64位长,则将输入值限制为32位长)然后,如果你想完全没有重叠或重复(没有A op B =与任何其他C op D相同的值的情况),你可以使用乘法运算符但是对于较小的潜在操作数集合中的每个值X,选择Xth素数作为乘法运算数。这样,无论你随机选择哪两个值A和B(从1到Max),操作都将是两个不同质数的乘法。这意味着可能的操作数集必须小于等于或小于您用于操作数的最大可能值的素数集(如果它是64位无符号长,即2 ^ 64)

第二次编辑:根据具体问题,可能的操作数集仅限于计算机屏幕的尺寸,远远小于长度的数量,(无论你在哪个平台上)所以非常简单明了保证每对可能的屏幕cooridinates将生成一个独特和不同的种子密钥的方法是简单地左移一个坐标的值足以保证与另一个坐标没有按位重叠,然后按位或结果与另一个坐标。

因此,如果您的屏幕是3000x3000,那么长lngVal =(x <12 | y)也会以最小的计算开销完成。