最適な通貨単位を見つけるアルゴリズム


8

マークは物事を過剰に考える傾向がある人々が住む小さな国に住んでいます。ある日、国の王は、変更をより効率的にするために国の通貨を再設計することを決定しました。王は、最小の紙幣の金額まで(ただし含まない)の金額を正確に支払うために必要なコインの予想数を最小限に抑えたいと考えています。

通貨の最小単位がコインであるとします。王国で最小の紙幣はコインの価値があります。王は、流通しているコインの金種が超えてはならないと決定しました。次に、問題は、を最小化するから整数のセットを見つけることは。nmm{d1,d2,...,dm}{1,2,...,n1}1n1i=1n1c1(i)+c2(i)+...+cm(i)c1(i)d1+c2(i)d2+...cm(i)dm=i

たとえば、標準のUSDとそのコインの金額をます。ここでは、最小の紙幣は最小のコインの100の価値があります。この通貨を使用して46セントを作るには4コインが必要です。我々は。ただし、コインの額面が場合、3つのコインのみが必要になります:。これらの金種セットのうち、99セントまでの合計を作るためにコインの平均数を最小化するものはどれですか。{1,5,10,25,50}c1(46)=1,c2(46)=0,c3(46)=2,c4(46)=1,c5(46)=0{1,15,30}c1(46)=1,c2(46)=1,c3(46)=1

より一般的には、と与えられた、どのようにしてアルゴリズムで最適なセットを決定できるでしょうか?明らかに、実行可能なすべてのサブセットを列挙し、1からまでの合計を計算するために必要な平均コイン数を計算し、途中で最適なものを追跡することができます。周囲に存在するので、 -subsets(生存可能であるが、それでもないすべてが)、これはそれほど効率的ではないであろう。あなたはそれよりうまくできますか?nmmn1C(n1,m) m


m <n-1の場合、ソリューションは常に正確にmの額面を持つことになりますか?(k <m <n-1)のkコインを使用するソリューションがある場合、そのための金種を追加することで、0から1までのカウントに対して常に1つのコインカウントを削減でき、平均を削減できます。それが本当なら、それは単純なランタイムを減らしますか?
マイクサミュエル

@MikeSamuelかしこまりました。ただし、2つの同等に優れたソリューションがあり、1つは金種で、もう1つは金種である場合、それは王が知ることに興味があるかもしれません。結局、より多くの種類のコインを作るにはお金がかかります。mk<m
Patrick87

m <n-1の場合、上記の合計だけで定義される2つの同等に良い解決策はあり得ないと思います。1 <= kが<N、次に加算の要素が1であると、コインの価値kが存在しない場合、加算の要素が> 1であることを硬貨価値のKがある場合
マイクサミュエル

@MikeSamuel私はそれはおそらく本当だと思いますが、もう一度言いますが、おそらく何らかのモチベーションを持って、それを回答の一部として見てみたいと思います。セットは(ほとんど)重複しないため、実際には少し複雑になります。
Patrick87

ソリューションスペースを狭めるもう1つの事実は次のとおりです。1の価値があるコインはすべてのソリューションに出現する必要があります。
マイクサミュエル

回答:


6

これは、よく知られた変更の問題に関連しています。非常によく研究されており、実際、この質問はブルートフォースを使用して [1] について調査されています。2003年の時点で、最適な金種を見つけることの難しさは未解決の問題のようです。m7

Shallit を引用している記事をチェックすると、貪欲な変化生み出す戦略を可能にする宗派が特に興味があるかのようです。そのような宗派が実際に有利であることは明らかです。


  1. この国に必要なものはジェフリーシャリットによる18cピース (2003)

2

私は(間違っていますが、我慢してください)コインの系列が最適であることを推測しました。コインは指数関数的に間隔が置かれるため、追加されたコインごとに可能な限り残りの値を減らします。あなたの例では、これはます。{bi| b=n1/m,0i<m}{1,3,9,27,81}

これは、ノッチより良い(ある)USDの宗派(より)、それは平均何もする必要はありません。390/99420/99

私は今、分析的にこれにアプローチする方法がわからないので、力ずくでいくつかの数を取得するためにハッキーなHaskellスクリプトを書きました。
指数分布が常に最良であるとは限りません。たとえば、、に対してが得られることがあります。が、用。私の遅いマシンは到達できないので、ここでは小さい数を使用する必要があります。(m,n)=(4,30)75/29{20,8,3,1}87/29{27,9,3,1}(5,100)

しかし、エラーはかなり小さいように見えました。ほとんどの場合、合計の除算は1.0 ...で始まる何かをもたらすので、さらにいくつかのテストを実行しました。

およびテストセットから、標準偏差の最良の解と比較して、指数関数的増加の平均誤差が得られます。3m56n401.120.085

テストパラメーターはかなり小さいと主張するかもしれませんが、指摘するように、設定すると、総当たりになるだけです(おそらくより優れた解決策がありますが、これは緩めていくつかのことを行う優れた言い訳でした) Haskell)。n=100


試してみたい場合は、ここが私のテストスイートです。

getopt :: [Integer] -> Integer -> [Integer]
getopt _ 0 = []
getopt coins target = choice:(getopt viable $ target - choice)
                          where
                            viable = filter ((>=) target) coins
                            choice = maximum $ viable

getsum :: [Integer] -> Integer -> Int
getsum coins n = sum $ map length $ map (getopt coins) [1..(n-1)]

buildall :: Integer -> Integer -> [[Integer]]
buildall 1 _ = [[1]]
buildall m n = foldl1 (++) $ map (\am -> map (\x -> x:am) [((head am)+1) .. (n-1)]) $ buildall (m-1) n

buildguess :: Integer -> Integer -> [Integer]
buildguess m n = reverse $ map ((^) $ ceiling $ (fromInteger n)**(1.0/(fromInteger m))) [0..(m-1)]

findopt :: Integer -> Integer -> ([Integer],Int)
findopt m n = foldl1 (\(l@(_,lhs)) -> (\(r@(_,rhs)) -> (if (lhs < rhs) then l else r)))
            $ map (\arr -> (arr,getsum arr n)) $ buildall m n

intcast :: (Integral a,Num b) => a -> b
intcast = fromInteger.toInteger

esterror :: Integer -> Integer -> Double
esterror m n = (intcast $ getsum (buildguess m n) n) / (intcast best)
                 where (_,best) = findopt m n

私はテストを実行しました

map (uncurry esterror) [(m,n) | m <- [3..5], n <- [6..40] ]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.