有一个给定号码N
,我必须找出repetitive division
和N给出商数的整数。
对于Ex:
N=8
Numbers Are 2 as: 8/2=4/2=2/2=1
5 as 8/5=1
6 as 8/6=1
7 and 8
我的诅咒:
从N/2+1
到N
的所有数字都为我提供商1
所以
Ans: N/2 + Check Numbers from (2, sqrt(N))
时间复杂度O(sqrt(N))
有没有更好的方法可以做到这一点,因为号码可以达到10^12
并且可以有很多查询?
可以是O(1)
还是O(40)
(因为2 ^ 40超过10 ^ 12)
答案 0 :(得分:0)
首先,您的算法不是O(sqrt(N))
,因为您忽略了按每个选中的数字划分的次数。如果要检查的数字是k
,则获得结果之前的分割数(通过上述方法)是O(log(k))
。因此,复杂性变为N/2 + (log(2) + log(3) + ... + log(sqrt(N)) = O(log(N) * sqrt(N))
。
现在我们已经解决了这个问题,可以改进算法。请注意,通过重复划分,只有当1
k
时,您才能获得k^t <= N < 2 * k^t
的已检查号码t=floor(log_k(N))
。
即k^t <= N < 2 * k^(t+1)
时。请注意右侧的严格<
。
现在,为了弄清楚t
,您可以使用Newton-Raphson方法或Taylor系列来非常快速地获得对数,并提到复杂性度量here。我们称之为C(N)
。所以复杂性将是C(2) + C(3) + .... + C(sqrt(N))
。如果您可以忽略计算log
的费用,则可以将其设为O(sqrt(N))
。
例如,在上述情况下,N = 8:
2^3 <= 8 < 2 * 2^3
:1 floor(log_3(8)) = 1
和8
不满足3^1 <= 8 < 2 * 3^1
:0 floor(log_4(8)) = 1
和8
不满足4^1 <= 8 < 2 * 4^1
:0 4
其他来自5
,6
,7
和8
的{{1}} 8
来自这些号码。 请注意,我们不需要检查t=1
和3
,但我这样做是为了说明这一点。并且您可以验证4
中的每个数字是否满足上述不等式,因此需要添加。
如果使用这种方法,如果计算对数的复杂度可以忽略不计,我们就可以消除重复的划分并将复杂度降低到[N/2..N]
。
答案 1 :(得分:0)
如果您希望每个查询O(1)
查找,那么小于或等于10^12
的自然哈希表将是其他自然权力的哈希表,不会大于2,000,000个元素;通过在1到1,000,000的基础上迭代来创建它,增加所看到的键的值;根1,000,000...10,001
只需要平方;根10,000...1,001
只需要立方体;之后,如前所述,最小的根最多可以有40次操作。
表格中的每个值都代表基本/电源配置的数量(例如,512 -> 2
,对应于2^9
和8^3
。)
答案 2 :(得分:0)
用于验证功能和评估复杂性顺序的测试工具。
根据需要进行编辑 - 其维基
#include <math.h>
#include <stdio.h>
unsigned long long nn = 0;
unsigned repeat_div(unsigned n, unsigned d) {
for (;;) {
nn++;
unsigned q = n / d;
if (q <= 1) return q;
n = q;
}
return 0;
}
unsigned num_repeat_div2(unsigned n) {
unsigned count = 0;
for (unsigned d = 2; d <= n; d++) {
count += repeat_div(n, d);
}
return count;
}
unsigned num_repeat_div2_NM(unsigned n) {
unsigned count = 0;
if (n > 1) {
count += (n + 1) / 2;
unsigned hi = (unsigned) sqrt(n);
for (unsigned d = 2; d <= hi; d++) {
count += repeat_div(n, d);
}
}
return count;
}
unsigned num_repeat_div2_test(unsigned n) {
// number of integers that repetitive division with n gives quotient one.
unsigned count = 0;
// increment nn per code' tightest loop
...
return count;
}
///
unsigned (*method_rd[])(unsigned) = { num_repeat_div2, num_repeat_div2_NM,
num_repeat_div2_test};
#define RD_N (sizeof method_rd/sizeof method_rd[0])
unsigned test_rd(unsigned n, unsigned long long *iteration) {
unsigned count = 0;
for (unsigned i = 0; i < RD_N; i++) {
nn = 0;
unsigned this_count = method_rd[i](n);
iteration[i] += nn;
if (i > 0 && this_count != count) {
printf("Oops %u %u %u\n", i, count, this_count);
exit(-1);
}
count = this_count;
// printf("rd[%u](%u) = %u. Iterations:%llu\n", i, n, cnt, nn);
}
return count;
}
void tests_rd(unsigned lo, unsigned hi) {
unsigned long long total_iterations[RD_N] = {0};
unsigned long long total_count = 0;
for (unsigned n = lo; n <= hi; n++) {
total_count += test_rd(n, total_iterations);
}
for (unsigned i = 0; i < RD_N; i++) {
printf("Sum rd(%u,%u) --> %llu. total Iterations %llu\n", lo, hi,
total_count, total_iterations[i]);
}
}
int main(void) {
tests_rd(2, 10 * 1000);
return 0;
}
答案 3 :(得分:-1)
让我们看看号码可以达到10^12
,你可以做的是为号码2 to 10^6
创建,你可以创建40
的数组,所以对于每个长度检查号码是否可以表示为i^(len-1)+ y
,其中i
介于2到10 ^ 6之间,而len介于1
到40
之间。
所以时间复杂度O(40*Query)