迭代次数的任务

Task on the number of iterations

本文关键字:任务 迭代      更新时间:2023-10-16

有一个数字N 每次迭代它都等于 (N*2(-1 我需要找出该数字将是原始 N 的倍数多少步;

( 1≤ N ≤ 2 · 10 9 (

例如:

N = 7; count = 0
N_ = 7*2-1 = 13; count = 1; N_ % N != 0
N_ = 13*2-1 = 25; count = 2; N_ % N != 0
N_ = 25*2-1 = 49; count = 3; N_ % N == 0 

答案是 3

如果无法以这种方式分解,则输出 -1

#include <iostream> 
using namespace std;
int main(){
int N,M,c;
cin >> N;
if (N%2==0) {
cout << -1;
return 0;
}
M = N*2-1;
c = 1;
while (M%N!=0){
c+=1;
M=M*2-1;
}
cout << c;
return 0;
}

它不适合(1 秒限制(。如何优化算法?

P.S 所有指示的答案都经过优化,但它们不适合 1 秒,因为原则上您需要更改算法。解决方案是使用欧拉定理。

正如其他答案所建议的那样,该问题等同于找到c,以便pow(2, c) = 1 mod N.如果 N 是偶数,这是不可能的,否则可能(正如您的代码所暗示的那样(。

线性时间方法是:

int c = 1;
uint64_t m = 2;
while (m != 1){
c += 1;
m = (2*m)%N;
}
printf("%dn", c);

为了在 1 秒内解决这个问题,我认为你不能使用线性时间算法。最糟糕的情况将是N是黄金和大的时候。例如1999999817,上面的代码在我的笔记本电脑上运行大约 10 秒。

相反,因素N其主要因素。求解每个素因数的2^c = 1 mod p^k(其中 p^k 出现在 N 的素因数分解中。然后使用中国余数定理组合结果。

当找到给定主功率的c时,如果k=1,则解c=p-1。当k较大时,细节相当混乱,但你可以在这里找到书面解决方案:https://math.stackexchange.com/questions/1863037/discrete-logarithm-modulo-powers-of-a-small-prime

问题是你溢出了,int 数据类型只有 32 位,溢出 2^31-1 ,在这个问题中你不需要保留 M 的实际值,你可以只保留 n 的模。

while (M%N!=0){
c+=1;
M=M*2-1;
M%=N
}

编辑:此外,您实际上不需要超过 N 次迭代来检查是否存在 0 mod,因为只有 N 个不同的 mod 到 N 并且它只是保持循环。 所以你还需要记住这一点,以防万一没有 0 mod。

毫无疑问,您的代码的主要问题是有符号整数溢出

每当更改M时,我都会添加M打印(即cout << M << endl;(并给了它输入 29。这就是我得到的:

57
113
225
449
897
1793
3585
7169
14337
28673
57345
114689
229377
458753
917505
1835009
3670017
7340033
14680065
29360129
58720257
117440513
234881025
469762049
939524097
1879048193
-536870911
-1073741823
-2147483647
1
1
1
1
... endless loop

如您所见,您已经签名整数溢出。这是 C 中未定义的行为,所以任何事情都可能发生!!在我的机器上,我最终得到了一个令人讨厌的无限循环。在考虑性能之前,必须解决此问题。

简单的解决方法是添加一行,例如

M = M % N;

每当M发生变化时。查看@Malek的答案

除此之外,您还应该使用无符号整数,即对所有变量使用uint32_t

但是,这不会提高性能。

如果在上述修复后仍然遇到性能问题,可以尝试以下操作:

uint32_t N;
cin >> N;
if (N%2==0) {
cout << -1;
return 0;
}
// Alternative algorithm
uint32_t R,c;
R = 1;
c = 1;
while (R != N){
R = 2*R + 1;
if (R > N) R = R - N;
++c;
}
cout << c;

在我的笔记本电脑上,在测试1..100000范围内的所有奇数时,该算法的速度提高了2.5倍。但是,对于 1..2*10^9 范围内的所有数字,这可能还不够。

另请注意使用uint32_t以避免整数溢出。