迭代次数的任务
Task on the number of iterations
有一个数字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
以避免整数溢出。
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 迭代时从向量和内存中删除对象
- 如何在c++迭代器类型中包装std::chrono
- 带过滤器的现代迭代c++集合
- 在c++中检查长方体是否尽可能快地重叠(无迭代)
- C++矢量迭代
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 擦除while循环中迭代的元素
- 实现一个在集合上迭代的模板函数
- 对于set上的循环-获取next元素迭代器
- 在向量内的向量上迭代
- 为什么output_editor Concept不需要output_e迭代器标记
- TSP递归解的迭代形式
- 迭代次数的任务
- HPX 是否提供具有粒度控制的基于任务的并行化迭代函数?
- 任务在删除其中一个迭代对象时会导致段错误
- 原生C++中的并行同步迭代任务