編集:問題の明確な説明
次の問題を解決する高速なアルゴリズムはありますか?
また、この問題の自然数を Z/(2^n Z) に置き換えた拡張版でもあるのでしょうか?(この問題は複雑すぎて、IMO という 1 か所に質問を追加することはできませんでした。)
問題:
{7, 20, 17, 100} のような特定の自然数のセットに対して、必要なアルゴリズムは加算、乗算、べき乗の最短シーケンスを返し、与えられたすべての数値を計算します。シーケンスの各項目は、次のパターンに一致する (正しい) 方程式です。
<number> = <number> <op> <number>
<number> は自然数、<op> は {+、*、^} のいずれかです。
シーケンスでは、<op> の各オペランドは次のうちの 1 つである必要があります。
- 1
- equal の左辺にすでに現れている数。
例:
Input: {7, 20, 17, 100}
Output:
2 = 1 + 1
3 = 1 + 2
6 = 2 * 3
7 = 1 + 6
10 = 3 + 7
17 = 7 + 10
20 = 2 * 10
100 = 10 ^ 2
Haskell でバックトラッキング アルゴリズムを書きました。上記のような小さな入力に対しては機能しますが、私の実際のクエリは [0,255] にランダムに分散された ~30 の数字です。実際のクエリの場合、次のコードは私の PC で 2 ~ 10 分かかります。
私の現在の(疑似)コード:
-- generate set of sets required to compute n.
-- operater (+) on set is set union.
requiredNumbers 0 = { {} }
requiredNumbers 1 = { {} }
requiredNumbers n =
{ {j, k} | j^k == n, j >= 2, k >= 2 }
+ { {j, k} | j*k == n, j >= 2, k >= 2 }
+ { {j, k} | j+k == n, j >= 1, k >= 1 }
-- remember the smallest set of "computed" number
bestSet := {i | 1 <= i <= largeNumber}
-- backtracking algorithm
-- from: input
-- to: accumulator of "already computed" number
closure from to =
if (from is empty)
if (|bestSet| > |to|)
bestSet := to
return
else if (|from| + |to| >= |bestSet|)
-- cut branch
return
else
m := min(from)
from' := deleteMin(from)
foreach (req in (requiredNumbers m))
closure (from' + (req - to)) (to + {m})
-- recoverEquation is a function converts set of number to set of equation.
-- it can be done easily.
output = recoverEquation (closure input {})
追記:
のような答え
- 高速なアルゴリズムはありません。なぜなら...
- ヒューリスティックなアルゴリズムがあり、それは...
も大歓迎です。今、高速で正確なアルゴリズムはないと感じています...
回答#1はヒューリスティックとして使用できると思います。