找到三个整数,使它们的余弦值之和达到最大值

时间:2018-12-01 09:36:32

标签: python optimization numbers combinations

有三个整数xyz(每个> = 1)和给定的上限整数n <10 ^ 6。另外,n = x + y + zoutput = 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) 

5 个答案:

答案 0 :(得分:13)

正如让·弗朗索瓦·法布尔(Jean-FrançoisFabre)在评论中指出的那样,您可以使用许多技巧来提高性能,但首先要

  • 注意到ab的值确定c的值,
  • 请注意,三个变量WLOG a中至少有一个小于或等于N/3
  • 使用bc中剩余的对称性将b限制在1和(N - a)//2 + 1之间
  • 预先计算cos的所有相关值,并尝试避免快速连续查找相同的值,
  • 使用Numba对代码进行JIT编译并免费获得一些性能(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是否为整数以减少样本空间。