C++ 在经过几次乘法**和溢出**后,是否可能获得数字的原始值?
总结:有没有办法做到这一点?我的意思是:假设我有一个无符号整数。然后我将它乘以几次(这里有溢出,,这是预期的)。那么是否可以“还原”原始值C++ 在经过几次乘法**和溢出**后,是否可能获得数字的原始值?,c++,c,algorithm,hash,modular,C++,C,Algorithm,Hash,Modular,总结:有没有办法做到这一点?我的意思是:假设我有一个无符号整数。然后我将它乘以几次(这里有溢出,,这是预期的)。那么是否可以“还原”原始值 详情如下: 都是关于你的。我需要做的是:我有一个长字符串的散列-例如:“abcd”。然后我得到了一个较短子字符串的哈希值,例如“cd”。如何使用给定的两个散列计算O(1)的“ab”散列 我现在的算法是: hash_t fast_pow( hash_t source, hash_t pow ) { if( 0 == pow ) {
详情如下: 都是关于你的。我需要做的是:我有一个长字符串的散列-例如:“abcd”。然后我得到了一个较短子字符串的哈希值,例如“cd”。如何使用给定的两个散列计算O(1)的“ab”散列 我现在的算法是:
hash_t fast_pow( hash_t source, hash_t pow )
{
if( 0 == pow )
{
return 1;
}
if( 0 != pow % 2 )
{
return source * fast_pow( source, pow - 1 );
}
else
{
return fast_pow( source * source, pow / 2 );
}
}
- 从“abcd”散列中减去“cd”散列(从多项式中删除最后的元素)
- 通过
划分“abcd”散列,其中p^len(“cd”)
是基数(素数)李>p
a*p^3+b*p^2+c*p^1+d*p^0
-abcd
c*p^1+d*p^0
-cd
ab获取:
(
( a * p ^ 3 + b * p ^ 2 + c * p ^ 1 + d * p ^ 0 ) -
( c * p ^ 1 + d * p ^ 0 )
)
/ ( p ^ 2 )
= a * p ^ 1 + b * p ^ 0
(
(a*p^3+b*p^2+c*p^1+d*p^0)-
(c*p^1+d*p^0)
)
/(p^2)
=a*p^1+b*p^0
如果我没有溢出(如果p
是一个小数字),这就行了。但是如果它不-它不工作
有什么把戏吗
另外,
c++
标记是因为数字溢出,因为它是特定的(并且不同于python、scheme或其他东西)所以溢出实际上就是你的编译器对你很好;C/+++标准实际上表明溢出是未定义的行为。所以一旦你飞越了,实际上你什么都做不了,因为你的程序不再是确定性的
您可能需要重新考虑算法,或者采用模运算/减法来修正算法。不知道溢出部分,但有一种方法可以恢复原始值 中国剩余定理帮助很大。让我们调用
h=abcd-cd
。G是值,h
,没有溢出,G=h+k*2^32
,假设溢出只发生%2^32
。因此,ab=G/p^2
G = h (mod 2^32)
G = 0 (mod p^2)
如果p^2和2^32是互质。这一页上,给我们
G = h * b * p^2 (mod 2^32 * p^2)
其中,b
是p^2模2^32的模乘逆,b*p^2=1(mod 2^32)
。计算G
后,只需除以p^2
即可找到ab
我希望我没有犯任何错误…您应该使用无符号整数来定义溢出行为(模2^N)。有符号整数溢出未定义
同样,你应该用p模的乘法逆乘以适当的值,而不是除以。例如,如果p=3且散列值为8位,则乘以171,因为171*3=513=2*256+1。如果p和模值是相对素数,则存在乘法逆。这里只是一个部分的答案:我认为严格来说,不必使用无符号整数。你可以用 但是请注意,对于-0和+0,这将有一个单独的表示形式,并且您可能需要在此过程中手工编码算术运算
某些处理器指令与整数表示无关,但并非所有指令。您有一个*b=c mod 2^32(或根据您的哈希处理方式修改其他指令)。如果你能找到d,使得b*d=1模2^32(或者其他模),那么你可以计算a*b*d=a 并检索a。如果gcd(b,mod 2^32)=1,则可以使用来查找x和y,使b*x+2^32*y=1,或 b*x=1-y*2^32,或
b*x=1 mod 2^32,因此x是要乘以的数字。扩展欧几里德算法是一个很好的解决方案,但它太复杂且难以实现。有一个更好的
还有另一种方法可以做到这一点(感谢我的电子朋友(:) 在-模乘逆中有一篇很好的文章,在
m
和a
是互质的情况下使用了欧拉定理:
其中φ(m)
为
在我的例子中,m
(模)是散列类型的大小-2^32
,2^64
,等等(在我的例子中是64位)。这意味着,我们应该只找到φ(m)的值,但是考虑一下-
m==2^64
,这就保证了m
将是所有奇数的互质,而不是任何偶数的互质。所以,我们需要做的是得到所有值的数目,并将它们除以2
此外,我们知道,m
将是未签名的,否则我们将遇到一些问题。这给了我们这样做的机会:
hash_t x = -1;
x /= 2;
hash_t a_reverse = fast_pow( a, x );
嗯,大约64位的数字,x
确实是一个很大的数字(19位:922372036854775807
),但是fast\u pow
非常快,我们可以缓存反向数字,以防我们需要多个查询
fast\u pow
是一种众所周知的算法:
hash_t fast_pow( hash_t source, hash_t pow )
{
if( 0 == pow )
{
return 1;
}
if( 0 != pow % 2 )
{
return source * fast_pow( source, pow - 1 );
}
else
{
return fast_pow( source * source, pow / 2 );
}
}
补充:例如:
hash_t base = 2305843009213693951; // 9th mersenne prime
hash_t x = 1234567890987654321;
x *= fast_pow( base, 123456789 ); // x * ( base ^ 123456789 )
hash_t y = -1;
y /= 2;
hash_t base_reverse = fast_pow( base, y );
x *= fast_pow( base_reverse, 123456789 ); // x * ( base_reverse ^ 123456789 )
assert( x == 1234567890987654321 ) ;
完美且非常快速。对于
p=2
这是不可能的。对于所有其他素数p
,这是可能的…@Sven Marnach-嗯,怎么做?我不能减去最后一个字母,然后除以基数(p
),再减去前面的最后一个字母,再除以p
,等等,因为我不知道字符串,但只知道它们的哈希值。@Sven Marnach-还有,有什么可能?要“还原”数字还是用来计算散列?如果是第二个,我想我需要接受cnicutar的答案,并提出关于散列的新问题?它是无符号整数,我刚刚编辑了这个问题。嗯,听起来很合理,但我知道,找到这样的逆元素绝非一个简单的问题,而且会让事情变慢..+1。我会考虑一个关于这一点。无符号值不会溢出。你的答案是什么