web-dev-qa-db-ja.com

scipy.optimize.minimizeを整数値に制限する

私はscipy.optimize.minimizeを使用して、答えが整数のみである可能性がある現実の問題を最適化しています。私の現在のコードは次のようになります:

from scipy.optimize import minimize

def f(x):
    return (481.79/(5+x[0]))+(412.04/(4+x[1]))+(365.54/(3+x[2]))+(375.88/(3+x[3]))+(379.75/(3+x[4]))+(632.92/(5+x[5]))+(127.89/(1+x[6]))+(835.71/(6+x[7]))+(200.21/(1+x[8]))

def con(x):
    return sum(x)-7

cons = {'type':'eq', 'fun': con}

print scipy.optimize.minimize(f, [1,1,1,1,1,1,1,0,0], constraints=cons, bounds=([0,7],[0,7],[0,7],[0,7],[0,7],[0,7],[0,7],[0,7],[0,7]))

これにより、

x: array([  2.91950510e-16,   2.44504019e-01,   9.97850733e-01,
     1.05398840e+00,   1.07481251e+00,   2.60570253e-01,
     1.36470363e+00,   4.48527831e-02,   1.95871767e+00]

しかし、私はそれを整数値で最適化したいと思います(すべてのxを最も近い整数に丸めることが常に最小値を与えるとは限りません)。

整数値のみでscipy.optimize.minimizeを使用する方法はありますか?

(私はxの可能なすべての順列で配列を作成し、各組み合わせに対してf(x)を評価することができると思いますが、それは非常にエレガントまたは迅速なようには見えません解決。)

15
nicmet

あなたの問題を助けるかもしれない一つのこととして、あなたは次のような制約を持つことができます:

max([x-int(x)])=0

これはあなたの問題を完全に解決するつもりはありません、アルゴリズムはまだ試行錯誤し、ある程度のエラーのある値を取得します~±5e-10 scipyのアルゴリズムのエラーだけで最適化を試みますが、何もしないよりはましです。

cons = ({'type':'eq', 'fun': con},
        {'type':'eq','fun': lambda x : max([x[i]-int(x[i]) for i in range(len(x))])})

このプロセスを私が解決策を知っているいくつかの最適化でテストしたところ、このプロセスは制約のない検索よりも初期値に敏感であり、かなり正確な答えが得られますが、実際には解決策が真の値を見つけられない場合があり、基本的に大きなジャンプが必要です小さな増分では通常、次の数値に移動するのに十分ではないため、最適化プロセス(極小値に最適化されていないことを確認するために使用するもの)のサンプルスペースを検索します。

1
Simon Jones

ブルートフォースソリューションの汎用関数。 Jaradが述べたように、範囲は明示的にそう言っていますが、scipyは実際には整数だけでなく、浮動小数点数で関数を実行するため、scipyで総当たりよりもいくらか良い仕事をします

def brute(func, arg_ranges, finish=min):
if isinstance(arg_ranges, dict):
    args = {k:np.unique(np.hstack([a for r in rs for a in r]) if isinstance(rs, list) else [a for a in rs]) for k,rs in arg_ranges.items()}
    print(args)
    return finish([(dict(Zip(args.keys(), vs)), func(**dict(Zip(args.keys(), vs)))) for vs in itertools.product(*args.values())], key=lambda x: x[1])
Elif isinstance(arg_ranges, list):
    return finish([(i, func(i)) for r in arg_ranges for i in r], key=lambda x: x[1])
else:
    return finish([(i, func(i)) for i in arg_ranges], key=lambda x: x[1])

print(brute(lambda x,y: x / (y + 2), {'x':[range(1,5,2), range(0,6,1)], 'y':range(2,5,1)}))