有三个整数x
,y
和z
(每个> = 1)和给定的上限整数n
<10 ^ 6。另外,n = x + y + z
和output = cos(x) + cos(y) + cos(z)
。
我们的工作是最大化output
。
为此我编写了一个简单的脚本,但是时间复杂度为O(n ^ 3)。有什么方法可以简化吗?
from math import cos
n = 50
x = 1
y = 1
z = 1
total = cos(x) + cos(y) + cos(z)
for x in xrange(n):
for y in xrange(n):
for z in xrange(n):
if x + y + z == n:
temp = cos(x) + cos(y) + cos(z)
if temp > total: total = temp
print round(total, 9)
答案 0 :(得分:13)
正如让·弗朗索瓦·法布尔(Jean-FrançoisFabre)在评论中指出的那样,您可以使用许多技巧来提高性能,但首先要
a
和b
的值确定c
的值,a
中至少有一个小于或等于N/3
,b
和c
中剩余的对称性将b
限制在1和(N - a)//2 + 1
之间N = 500
的系数约为400,然后,对于N = 1000000
(否则对于任何给定的N < 1000000
),否则蛮横的解决方案就会相对迅速地终止:
import numpy as np
from numba import jit
@jit
def maximize(N):
cos = np.cos(np.arange(N))
m = -3
for a in range(1, N//3 + 1):
for b in range(1, (N - a)//2 + 1):
c = N - a - b
res = cos[a] + cos[b] + cos[c]
if res > m:
m = res
bestabc = (a, b, c)
return m, bestabc
maximize(1000000) # (2.9787165245899025, (159775, 263768, 576457))
答案 1 :(得分:7)
理想情况下,您只希望一次计算每种可能的组合。忽略cos
的几何属性,并将其视为简单的数字到数字的映射(例如,将其用作随机属性,如@Jean在他的第二条评论中所述)。
首先,您必须意识到选择2个数字后,将给出第三个数字。您可以选择“智能”以避免重复的选择:
from math import cos
import time
import numpy as np
from numba import jit
def calc(n):
x = 1
y = 1
z = 1
total = cos(x) + cos(y) + cos(z)
for x in range(n, int((n/3 - 1)),-1): #I only want to pick X from n-2 to n/3 -1 , after that we will repeat.
cosx = cos(x)
for y in range(max(int(((n-x)/2))-1,1),min(int(n-x),int(n/3))): #I would only pick number that will not be choosen for the z
z = n-x-y #Infer the z, taking the rest in account
temp = cosx + cos(y) + cos(z)
if temp > total: total = temp
return total
tic = time.clock()
total = calc(10000)
print(time.clock()-tic)
print (total)
将1.3467099999999999
(在我的计算机上)使用。
正如@fuglede所述,值得使用numba进行进一步优化。
编辑:
保存所有先前计算的cos值实际上要比重新计算它们昂贵得多,当您访问np数组时,您不仅在访问内存中的点,还使用了ndarray函数。使用内置的python cos
实际上更快:
import numpy as np
from math import cos
import time
import timeit
cos_arr = np.cos(np.arange(10000000))
tic = time.time()
def calc1():
total = 0
for j in range(100):
for i in range(10000000):
total += cos_arr[i]
def calc2():
total = 0
for j in range(100):
for i in range(10000000):
total += cos(i)
time1 = timeit.Timer(calc1).timeit(number=1)
time2 = timeit.Timer(calc2).timeit(number=1)
print(time1)
print(time2)
输出:
127.9849290860002
108.21062094399986
如果我将数组创建移到计时器内部,则速度会更慢。
答案 2 :(得分:3)
绝对不需要计算3 x n ^ 3余弦值。
我们可以假设x≤y≤z。因此,x可以是1到n / 3范围内的任何整数。 y可以是x到(n-x)/ 2范围内的任何整数。z必须等于n-x-y。仅此一项就可以将您尝试的三元组(x,y,z)的数量从n ^ 3减少到大约n ^ 2 /6。
接下来,假设您找到了三个数字,总计2.749。然后您尝试使用余弦(x)= 0.748的x。涉及此x的任何总数不能超过2.748,因此您可以直接拒绝x。一旦找到一个好的总和,就可以拒绝许多x值。
要使其更有效,请对余弦(x)的值x从最高值到最低值进行排序,因为这更有可能找到较高的总计,从而可以删除更多的值。
并且计算cos(x)的速度很慢,因此您需要将值存储到表中。
所以:
Set c[i] = cos (i) for 1 ≤ i ≤ n.
Set x[i] = integers 1 to n/3, sorted in descending order by value of c[i].
Set (bestx, besty, bestz) = (1, 1, n-2) and total = c[bestx] + c [besty] + c [bestz].
for x = elements of array x where c[x] + 2 ≥ bestTotal
for y = x to (n-x)/2
z = n - x - y
total = c[x] + c[]y] + c[z]
if total > bestTotal
(bestx, besty, bestz) = (x, y, z)
bestTotal = total
您可以通过一些数学来改善这一点。如果y + z的和是恒定的,例如y + z = n-x,则cos(y)+ cos(z)的和是有限的。设P为最接近(n-x)/ 2pi的整数,设d =(n-x)-P * 2pi,则cos(y)+ cos(z)的最大和为2 * cos(d / 2)。
因此,对于每个x,1≤x≤n / 3,我们计算该值d和cos(x)+ 2 * cos(d / 2),并将这些值存储为某些x可以实现的最大总和,对x进行排序,以使这些值按降序排列,而忽略那些x可以达到的总数小于到目前为止的最佳总数的x。
如果n确实很大(比如说十亿),那么您可以使用Euclid算法快速找到所有接近2k * pi + d的整数y,但这会有些复杂。
for x in 1 to n/3
let s = n - x
let P = s / 2pi, rounded to the nearest integer
let d = (s - P * 2pi) / 2
let maxSum [x] = cos(x) + 2*cos(d)
Set x[i] = integers 1 to n/3, sorted in descending order by value of maxSum[i].
Set (bestx, besty, bestz) = (1, 1, n-2)
Set bestTotal = c[bestx] + c [besty] + c [bestz].
for x = elements of array x where maxSum[x] ≥ bestTotal
for y = x to (n-x)/2
z = n - x - y
total = c[x] + c[]y] + c[z]
if total > bestTotal
(bestx, besty, bestz) = (x, y, z)
bestTotal = total
PS。实际上,我以大约1亿个N的值尝试了此操作。事实证明,我可以对数组进行排序以首先尝试x的最有前途的值,这要花很长时间,但是通常x的第一个值是唯一尝试过的值。或者我可以使用x = 1、2、3等。这意味着将尝试x的几十个值,这比排序快。
答案 3 :(得分:1)
无需计算余弦来回答这个问题。只需跟踪函数的三个最小值(如果允许n = 0,则为两个)的最小值
f(n) = abs(2pi*n-round(2pi*n))
n
从1到N,其中N是您的搜索上限。
余弦在2*pi
的倍数处为1,因此我们在搜索范围内搜索最接近整数的两个或三个倍数。
还没有运行程序来执行此操作,但是在任何编程语言中都应该很容易。我将使用Mathematica。
答案 4 :(得分:-1)
这纯粹是一个基本的三角学问题。等式的最大值将是所有余弦值均为1时。在cos(n)中,对于由n = 2 * pi * k的集合构成的所有值,其中n为任意数字> = 0并且k是整数;您的余弦值为1。您的x,y,z值属于该集合,这些值的排列将为您提供所需的值。 另外,不要忘记检查集合中的n是否为整数以减少样本空间。