所以我遇到了这个问题:
从1到1000有多少个数字不能被数字2,3和5整除?
起初看起来很简单,所以我写了一个快速的python程序来解决它:
#!/user/bin/env python3
import sqlite3
con = sqlite3.connect(':memory:')
cur = con.cursor()
cur.execute('CREATE TABLE test (name, age)')
cur.execute('INSERT INTO test VALUES (:name, :age)', {'name': 'Aaron', 'age': 75})
cur.execute('INSERT INTO test VALUES (:name, :age)', {'name': 'Zebedee', 'age': 5})
cur.execute('SELECT * FROM test ORDER BY age ASC')
results = cur.fetchall()
print('\nGood, but hard coded:\n', results)
# Good, but hard coded:
# [('Zebedee', 5), ('Aaron', 75)]
cur.execute('SELECT * FROM test ORDER BY :order_by ASC', {'order_by': 'age'})
results = cur.fetchall()
print('\norder_by parameter ignored:\n', results)
# order_by parameter ignored:
# [('Aaron', 75), ('Zebedee', 5)]
cur.execute('SELECT * FROM test ORDER BY {order_by} ASC'.format(order_by='age'))
results = cur.fetchall()
print('\nRight order, but vulnerable to SQL injection:\n', results)
# Right order, but vulnerable to SQL injection:
# [('Zebedee', 5), ('Aaron', 75)]
con.close()
我得到了正确的答案(266),但我认为如果我想检查的不仅仅是3个值,那么这样做是很多打字。我也想做一个数学解决方案,所以我遇到了这个:
count = 0
for number in range(1,1000):
if number % 2 != 0 and number % 3 != 0 and number % 5 != 0:
count += 1
print(count)
我认为这是一个很好的方法,所以我在代码中实现了它:
1000 - ((1000/2 +1000/3 +1000/5) -(1000/2x3 +1000/2x5 + 1000/3x5)+ (1000/2x3x5)) = 1000-((500+333+200) - (166 +100 + 66) + 33) = 1000- 734 = 266
现在我很确定我在第二个函数中的某个地方搞砸了,因为它似乎不适用于更多的数字。例如,如果我试图找到
从1到1000有多少个数字不能被数字2,3,5和7整除?
第一种方法返回def foo(ln = 1000), numbers = [2,3,5]:
div = 0
muldiv = 0
totdiv = 1
for n in numbers:
div += ln/n
for i in numbers:
for n in range(numbers.index(i)+1, len(numbers)):
muldiv += ln/(i * numbers[n])
for n in numbers:
totdiv *= n
answer = ln - (div - muldiv + ln/totdiv)
print("answer is ", math.floor(answer))
而228
返回foo(numbers = [2,3,5,7])
...我很确定300
是正确答案,因为还有一个数字意味着有228
较少的因素而不是更多,但我哪里出错了?有没有更好的方法来解决这个问题?
答案 0 :(得分:5)
你不需要算法,简单的数学就足够了:
假设您要计算从 k 分割的数字1到 N (包括)的数量,这相当于:
地板(N / k)的
因此,在这种情况下可分为3的数字是333。
现在你不能简单地使用计算可分为2,3和5的数字的数量;总结一下,因为有共同点。确实:例如,15和3都可以分割。
您可以使用inclusion-exclusion principle:
解决此问题可分为2,3和5的数字量与
相同因此,为了解决您的第一个问题,您可以简单地说明:
def n_div(N,k):
return N//k
def n_div235(N):
return n_div(N,2)+n_div(N,3)+n_div(N,5)-n_div(N,2*3)-n_div(N,2*5)-n_div(N,3*5)+n_div(N,2*3*5)
def not_div235(N):
return N-n_div235(N)
正如您所看到的,它会生成正确的结果:
>>> not_div235(1000)
266
只要 N 与除数的数量相比非常大,您最好使用包含 - 排除方法:
你可以这样做:
import itertools
from functools import reduce
import operator
def gcd(a, b):
while b:
a, b = b, a % b
return a
def lcm(a, b):
return a * b // gcd(a, b)
def lcm_list(ks):
res = 1
for k in ks:
res = lcm(res,k)
return res
def n_div_gen(N,ks):
nk = len(ks)
sum = 0
factor = 1
for i in range(1,nk+1):
subsum = 0
for comb in itertools.combinations(ks,i):
subsum += n_div(N,lcm_list(comb))
sum += factor * subsum
factor = -factor
return sum
def not_div_gen(N,ks):
return N-n_div_gen(N,ks)
对于小 N ,这不会得到回报,但是要说要计算可分为3,5和7的数字从1到1 000 000 000是:
>>> not_div_gen(1000000000,[3,5,7])
457142857
你可以这样做:
>>> sum(i%3!=0 and i%5!=0 and i%7!=0 for i in range(1,1000000001))
457142857
但是计算它需要几分钟,而我们自己的方法使用毫秒。请注意,这仅适用于巨大的N.
答案 1 :(得分:1)
将内置函数sum
和all
与嵌套生成器一起使用:
def f(r=1000, nums=(2,3,5)):
return sum(all(x%n for n in nums) for x in range(1, r+1))
这将遍历数字范围,检查每个数字是否具有每个指定数字的非零模数,并将这些布尔值相加(False
为0且True
为1) 。 nums
(2,3,5,7)
生成228的结果,这与您更短,更简单的代码(令人放心的是,不使用任何浮点运算)一致,作为您的第二个代码块一样)。
答案 2 :(得分:1)
直到N的整数不能被n 1 ,n 2 ,...,n t 整除(假设是(pairwise-coprime)是
最多N 减去的整数数量
(SUM i in 1..t (最多N的整数数除以n i )) plus
(SUM i,j in 1..t,i< j (最多N的整数数除以n i n j ) )减去
(SUM i,j,k in 1..t,i&lt; j&lt; k (最多N的整数个数除以n i n j < / sub> n k )) plus
(SUM i,j,k,l in 1..t,i&lt; j&lt; k&lt; l (最多N的整数数除以n i n < sub> j n k n l )) minus
...... ...... ......
(SUM i,j,k,l,... q in 1..t,i&lt; j&lt; k&lt; l&lt; 1&lt; ...&lt; q (最多N个整数的整数by n i n j n k n l ... n q ) )
系列继续,直到下标包含原始列表中的所有 t 整数。
对于未知为成对互质的数字,请用最不常见的倍数替换它们的产品。
这就是为什么您的方法仅适用于3个数字的原因。您只计算该系列的前四个成员。
答案 3 :(得分:1)
这是另一个使用包含 - 排除的实现。它比Willem Van Onsem的优秀答案(我在编写此代码之前没有看到)中的代码简单,但只有在除数列表中的数字全部都有效时才会有效彼此相互影响。对于更一般的情况,您需要使用Willem的方法。
from itertools import combinations
from functools import reduce
def prod(seq, mul=int.__mul__):
return reduce(mul, seq, 1)
def count_coprimes(n, divisors):
total = n
sign = -1
for i in range(1, len(divisors) + 1):
for k in combinations(divisors, i):
total += n // prod(k) * sign
sign = -sign
return total
print(count_coprimes(1000, [2, 3, 5]))
<强>输出强>
266
FWIW,这里的算法与&#34; one-liner&#34;相同。 (分成几行以提高可读性)。由于内循环中的(-1)**i
,它效率稍低。
def count_coprimes(n, divisors):
return n + sum(n // prod(k) * (-1)**i
for i in range(1, len(divisors) + 1)
for k in combinations(divisors, i))
print(count_coprimes(1000000000, [3, 5, 7]))
<强>输出强>
457142857
我们可以通过否定除数和使用修改的整数除法函数来摆脱(-1)**i
:
def div(p, q):
return p // q if q > 0 else -(p // -q)
def count_coprimes(n, divisors):
return sum(div(n, prod(k))
for i in range(len(divisors) + 1)
for k in combinations([-u for u in divisors], i))
答案 4 :(得分:0)
你可以做一个非常小的改变,大致减少所需的时间,而不是生成从1到1000的所有数字,生成从1到1000的所有奇数:
count = 0
for number in range(1,1001,2):
if number % 3 != 0 and number % 5 != 0:
count += 1
print(count)
虽然这不是一个巨大的变化,也不是一个数学解决方案,但它使代码的可读性和效率更低。
考虑到你的其他代码,你可以在if语句中使用列表理解来检查其他数字(注意我也使用第一个数字来生成数字的初始列表,而不是执行模数运算全部1000):
def foo(lst):
count = 0
for number in range(1,1001,lst[0]):
if not any([number % i == 0 for i in lst[1:]]):
count += 1
return count
>>> foo([2,3,5])
266
>>> foo([2,3,5,7])
228
答案 5 :(得分:0)
有许多方法可以迭代地解决这个小问题,所有这些方法都具有非常相似的性能,这里有几个例子:
import timeit
def f1(l, h):
count = 0
for number in range(l, h):
if number % 2 != 0 and number % 3 != 0 and number % 5 != 0:
count += 1
return count
def f2(l, h):
return len(filter(lambda x: x % 2 != 0 and x % 3 != 0 and x % 5 != 0, range(l, h)))
def f3(l, h):
count = 0
for number in range(l, h):
if number % 2 == 0:
continue
if number % 3 == 0:
continue
if number % 5 == 0:
continue
count += 1
return count
def f4(l, h):
return len([x for x in range(l, h) if x % 2 != 0 and x % 3 != 0 and x % 5 != 0])
a, b, N = 1, 1000, 10000
print timeit.timeit('f1(a,b)', setup='from __main__ import f1, a, b', number=N)
print timeit.timeit('f2(a,b)', setup='from __main__ import f2, a, b', number=N)
print timeit.timeit('f3(a,b)', setup='from __main__ import f3, a, b', number=N)
print timeit.timeit('f4(a,b)', setup='from __main__ import f4, a, b', number=N)
i7-2.6ghz上的时间将是:
0.802361558825
1.46568073638
0.91737188946
0.846404330893
通常这些时间足以在下限/上限(1,1000)相对较小时考虑。现在,如果我们谈论计算不可行的真正高边界(万亿),你可以考虑应用更聪明的inclusion-exclusion principle,这样你就可以解决问题并且你可以获得与您的解决方案保持一致的时间。
答案 6 :(得分:0)
<强>输入强>
让n
为要测试的区间<0,n>
,d[]={2,3,5,0};
为空终止的除数数组
计算d[]
最小公倍数是 SoE 重复自身的时间段。 2,3,5
的{{1}}是lcm=30
。在计算它时使用max(d[])
作为增量来提高速度......如果LCM太大(LCM>=n
),则使用n
代替速度。
计算<0,LCM)
只需创建 LCM 数字数组,并为不可分a[i]=1
和i
设置a[i]=0
以获得可分i
。
将SoE转换为不可分割的数字
只需计算a'[i]=a[0]+a[1]+..a[i]
计算点数
算是简单的:int(n/LCM)*a'[LCM-1] + a'[n%LCM];
这里简单的 C ++ 示例:
int non_divisibles(int n,const int *d) // SoE
{
int i,j,cnt,lcm,m,*a;
for (m=0;d[m];m++); // compute m
for (cnt=0,i=0;i<m;i++) if (cnt<d[i]) cnt=d[i]; // cnt = max d[] (to speed up LCM)
// LCM d[]
a=new int[m]; if (a==NULL) return -1;
for (i=0;i<m;i++) a[i]=d[i];
for (lcm=cnt;lcm<=n;lcm+=cnt) // no need to test above `n` even if lcm is bigger
{
for (i=0;i<m;i++) for (;a[i]<lcm;) a[i]+=d[i];
for (i=0;i<m;i++) if (a[i]!=lcm) { i=-1; break; }
if (i>=0) break;
}
delete[] a;
// SoE <0,LCM)
a=new int[lcm]; if (a==NULL) return -1;
for (i=0;i<lcm;i++) a[i]=1;
for (j=0;d[j];j++)
for (i=0;i<lcm;i+=d[j])
a[i]=0;
// convert to cnt
for (i=1;i<lcm;i++) a[i]+=a[i-1];
// compute whole count
cnt =(n/lcm)*a[lcm-1];
cnt+=a[n%lcm];
delete[] a;
return cnt;
}
这里有一些测量来比较天真, SoE 和 SoE (最大(n
, LCM ({{1} })))方法:
d[]
正如您所看到的, SoE(n)更好,如果 LCM 与n=1000000 d[]={ 2 3 5 7 11 13 17 19 }
171021 [ 27.514 ms] naive
171021 [ 12.642 ms] SoE
171021 [ 25.438 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 7 11 13 17 }
180524 [ 26.212 ms] naive
180524 [ 11.781 ms] SoE
180524 [ 9.807 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 7 11 13 }
191808 [ 24.690 ms] naive
191808 [ 11.512 ms] SoE
191808 [ 0.702 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 }
266666 [ 16.468 ms] naive
266666 [ 9.744 ms] SoE
266666 [ 0.006 ms] LCM+Soe
n= 1000 d[]={ 2 3 5 }
266 [ 0.012 ms] naive
266 [ 0.012 ms] SoE
266 [ 0.001 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 19 23 61 87 10001 }
237662 [ 26.493 ms] naive
237662 [ 10.180 ms] SoE
237662 [ 19.429 ms] LCM+Soe
相比过大(n
包含许多素数或大数字)但需要d[]
空间。
答案 7 :(得分:0)
一旦你可以分开就可以拯救。
266 not divisible by [2, 3, 5]
228 not divisible by [2, 3, 5, 7]
输出:
WebSocketContainer
答案 8 :(得分:0)
def not_divisible(n = 1000, divisors = [2, 3, 5]):
count = 0
for i in range(1, n + 1):
if all(1 if i % d else 0 for d in divisors):
count += 1
return count
第四行的说明:
这是一个较小的代码:
def not_divisible(n = 1000, divisors = [2, 3, 5]):
return sum(1 for i in range(1, n + 1) if all(1 if i % d else 0 for d in divisors))
在这里,我们为无法被所有除数除的每个数字生成一个1的列表,该列表的总和就是答案。