要素の配列M
、すべての数値、負または正またはゼロがあるとします。
N
これらの要素の合計がN
可能な限り最小の正の数になるように、配列から要素を選択するアルゴリズムを提案できる人はいますか?
たとえば、次の配列を使用します。
-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200
ここで、合計が可能な限り最小の正の数になるように、任意の 5 つの要素を選択する必要があります。
要素の配列M
、すべての数値、負または正またはゼロがあるとします。
N
これらの要素の合計がN
可能な限り最小の正の数になるように、配列から要素を選択するアルゴリズムを提案できる人はいますか?
たとえば、次の配列を使用します。
-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200
ここで、合計が可能な限り最小の正の数になるように、任意の 5 つの要素を選択する必要があります。
これは Haskell で最適化されていないものですが、(私のアイデアの多くと同様に) おそらくさらに最適化され、より適切に最適化される可能性があります。次のようになります。
数値が負 (昇順ソートの場合) または正 (降順ソートの場合) である配列の部分については、ステップ 3 を計算なしですぐに実行できることに注意してください。
出力:
*Main> least 5 "desc" [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
(10,[-1000,600,300,100,10])
(0.02 secs, 1106836 bytes)
*Main> least 5 "asc" [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
(50,[300,100,-200,-100,-50])
(0.02 secs, 1097492 bytes)
*Main> main -- 10000 random numbers ranging from -100000 to 100000
(1,[-106,4,-40,74,69])
(1.77 secs, 108964888 bytes)
コード:
import Data.Map (fromList, insert, (!))
import Data.List (minimumBy,tails,sort)
import Control.Monad.Random hiding (fromList)
array = [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
least n rev arr = comb (fromList listStart) [fst (last listStart) + 1..m]
where
m = length arr
r = if rev == "asc" then False else True
sorted = (if r then reverse else id) (sort arr)
listStart = if null lStart
then [(n,(sum $ take n sorted,take n sorted))]
else lStart
lStart = zip [n..]
. takeWhile (all (if r then (>0) else (<0)) . snd)
. foldr (\a b -> let c = take n (drop a sorted) in (sum c,c) : b) []
$ [0..]
s = fromList (zip [1..] sorted)
comb list [] = list ! m
comb list (i:is)
| fst (list ! (i-1)) == 1 = list ! (i-1)
| otherwise = comb updatedMap is
where updatedMap = insert i bestCandidate list
bestCandidate = comb' (list!(i - 1)) [i - 1,i - 2..n] where
comb' best [] = best
comb' best (j:js)
| fst best == 1 = best
| otherwise =
let s' = map (\x -> (sum x,x))
. (take n . map (take (n - 1)) . tails . cycle)
$ snd (list!j)
t = s!i
candidate = minimumBy compare' (map (add t) s')
in comb' (minimumBy compare' [candidate,best]) js
add x y@(a,b) = (x + a,x:b)
compare' a@(a',_) b@(b',_)
| a' < 1 = if b' < 1 then compare (abs a') (abs b') else GT
| otherwise = if b' < 1 then LT else compare a' b'
rnd :: (RandomGen g) => Rand g Int
rnd = getRandomR (-100000,100000)
main = do
values <- evalRandIO (sequence (replicate (10000) rnd))
putStrLn (show $ least 5 "desc" values)
最初の配列をすでにショートさせてください。そうしないと、ショートしていなくても機能すると思います..
N -> 配列の長さ
M -> 要素が必要です。
R[] -> Answer
TEMP[] -> 計算
用 minSum -> minSum
A[] -> 初期入力
上記の変数はすべてグローバルに定義されています
int find(int A[],int start,int left)
{
if(left=0)
{
//sum elements in TEMP[] and save it as curSum
if(curSum<minSum)
{
minSum=curSum;
//assign elements from TEMP[] to R[] (i.e. our answer)
}
}
for(i=start;i<=(N-left);i++)
{
if(left==M)
curSum=0;
TEMP[left-1]=A[i];
find(A[],i+1,left-1);
}
}
// 急いで作成したので、エラーが発生している可能性があります..
ideone の作業ソリューション: http://ideone.com/YN8PeW
Kadaneのアルゴリズムがうまくいくと思いますが、それは最大の合計のためですが、最小の合計を見つけるためにも実装しましたが、今はコードを見つけることができません。
可能な限り最善の解決策を見つけたい場合は、単純にブルートフォースを使用できます。考えられるすべての数字の組み合わせを試してください。
この非常に迅速で汚いアルゴリズムのようなもの:
public List<Integer> findLeastPositivSum(List<Integer> numbers) {
List<Integer> result;
Integer resultSum;
List<Integer> subresult, subresult2, subresult3, subresult4, subresult5;
for (int i = 0; i < numbers.size() - 4; i++) {
subresult = new ArrayList<Integer>();
subresult.add(numbers.get(i));
for (int j = i + 1; j < numbers.size() - 3; j++) {
subresult2 = new ArrayList<Integer>(subresult);
subresult2.add(j);
for (int k = j + 1; k < numbers.size() - 2; k++) {
subresult3 = new ArrayList<Integer>(subresult2);
subresult3.add(k);
for (int l = k + 1; l < numbers.size() - 1; l++) {
subresult4 = new ArrayList<Integer>(subresult3);
subresult4.add(k);
for (int m = l + 1; m < numbers.size(); m++) {
subresult5 = new ArrayList<Integer>(subresult4);
subresult5.add(k);
Integer subresultSum = sum(subresult5);
if (subresultSum > 0) {
if (result == null || resultSum > subresultSum) {
result = subresult;
}
}
}
}
}
}
}
return result;
}
public Integer sum(List<Integer> list) {
Integer result = 0;
for (Integer integer : list) {
result += integer;
}
return result;
}
これは非常に高速で汚れたアルゴリズムであり、よりエレガントに実行できます。再帰を使用するなど、よりクリーンなアルゴリズムを提供できます。
さらに最適化することもできます。たとえば、最初のステップとして入力リストから同様の番号を削除できます。