たけしのコマ大数学科 第18回:割り当て問題 – ガスコン研究所

パッとみて,たまたま答えが28位だなあって分かってしまったので.

全部試行しても5!=120通り.ランダムに試行すれば試行数はどうなるか.

import numpy as np

a = np.array([6, 5, 6, 5, 6])
b = np.array([8, 7, 6, 8, 7])
c = np.array([4, 5, 4, 4, 5])
d = np.array([6, 7, 6, 4, 7])
e = np.array([10, 8, 10, 7, 10])
ind = np.arange(5)

for i in range(1000):
    np.random.shuffle(ind)
    if a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]] < 29:
        print(f'Task {i}')
        print(a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]])
        print(ind)
        print(a[ind[0]], b[ind[1]], c[ind[2]], d[ind[3]], e[ind[4]])
        break

Task 18
28
[4 2 0 3 1]
6 6 4 4 8

%%timeit
ind = np.arange(5)
for i in range(1000):
    np.random.shuffle(ind)
    if a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]] < 29:
        #print(f'Task {i}')
        #print(a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]])
        #print(ind)
        #print(a[ind[0]], b[ind[1]], c[ind[2]], d[ind[3]], e[ind[4]])
        break

2.1 ms ± 232 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

1万回試して,試行数のボックスプロットをみる.

%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np


a = np.array([6, 5, 6, 5, 6])
b = np.array([8, 7, 6, 8, 7])
c = np.array([4, 5, 4, 4, 5])
d = np.array([6, 7, 6, 4, 7])
e = np.array([10, 8, 10, 7, 10])
ind = np.arange(5)

res = []
for _ in range(10000):
    for i in range(1000):
        np.random.shuffle(ind)
        if a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]] < 29:
            res.append(i)
            break

sns.boxplot(x=res)

FireShot Capture 273 - JupyterLab Alpha Preview - http___localhost_8888_lab

下限は0,上限は899回,およそ全試行数が期待される.

numpy使っているけど,numpy使っていないので,
(最初はちょっとテクニカルな事を考えていた……こんがらがって時間掛かりすぎたので断念)

この場合はnumpy使わない方が勿論速い.

import random

a = [6, 5, 6, 5, 6]
b = [8, 7, 6, 8, 7]
c = [4, 5, 4, 4, 5]
d = [6, 7, 6, 4, 7]
e = [10, 8, 10, 7, 10]
ind = list(range(5))

for i in range(1000):
    random.shuffle(ind)
    if a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]] < 29:
        print(f'Task {i}')
        print(a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]])
        print(ind)
        print(a[ind[0]], b[ind[1]], c[ind[2]], d[ind[3]], e[ind[4]])
        break

Task 99
28
[4, 2, 0, 3, 1]
6 6 4 4 8

%%timeit
ind = list(range(5))

for i in range(1000):
    random.shuffle(ind)
    if a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]] < 29:
        #print(f'Task {i}')
        #print(a[ind[0]] + b[ind[1]] + c[ind[2]] + d[ind[3]] + e[ind[4]])
        #print(ind)
        #print(a[ind[0]], b[ind[1]], c[ind[2]], d[ind[3]], e[ind[4]])
        break

1.06 ms ± 51.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

factorial(10) = 3628800

位なら,正直ランダムに総当りしても30秒位で解けるし(最悪でも3分程度),
(上のは線形時間だけど,numpy使って並列化すればもっと速くなる)
(後,ちゃんと調べていないので,また後日はっきりさせたいけど,)
(ループ処理の場合はnumpy駄目絶対,ピュアに書こうが常識だと思っていたけど,)
(ループ回数が1000,10,000回を超える様な処理の場合には,numpyの方が速いかも)
(ピュアな書き方は安定して線形時間なのに対して,numpyはほぼ線形時間だけど20%位速くなる)

factorial(100) = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

とかになってくると,何かしらの法則,制約条件が無ければどの様な方法でも解けないだろうし,
むしろヒューリスティックな方法が必要になってくるんだろう.

カテゴリー: 未分類 パーマリンク