I've been having a hard time solving this and also explaining it to people, but I will try: I have a float number and I want it rounded to the nearest combination of a few other given numbers.
I will now go straight into examples, otherwise I'm afraid I'll lose you:
Let's say our numbers to round up to are: 190, 290, 540, 1000
I'll provide some examples of given numbers and expected results just to make sure we are on the same page:
Given number: 54,6
Expected result: 190 (1x190)
Given number: 287,5
Expected result: 290 (1x290)
Given number: 575
Expected result: 580 (2x290)
Given number: 1150
Expected result: 1190 (1x1000 + 1x190)
Given number: 1955
Expected result: 2020 (1x1000 + 1x540 + 1x290 + 1x190)
Given number: 2875
Expected result: 3020 (2x1000 + 1x540 + 1x290 + 1x190)
So the point is to get the sum of values that equal or exceed the given number (and extract the one that exceeds the least?) I wrote a simple-minded function that somehow does what I want, but not exactly:
function roundUpBasedOnPackaging(neededAmount, packagingIntegerArray) {
console.log("We need to fill: " + neededAmount);
var roundTo = 0;
for (var len = packagingIntegerArray.length, i = len - 1; i >= 0; i--) {
var currentItemGrams = packagingIntegerArray[i];
//console.log("we are at " + currentItemGrams);
if (roundTo + currentItemGrams <= neededAmount) {
console.log("-- add: " + currentItemGrams);
roundTo += currentItemGrams;
i = len; // try this again
}
// if last and we'r not filled yet
else if (i == 0 && roundTo < neededAmount) {
console.log("-- add to get over neededAmount: " + currentItemGrams);
roundTo += currentItemGrams;
}
}
console.log("rounded to " + roundTo);
}
roundUpBasedOnPackaging(287.5, [190, 290, 540, 1000]);
You can clearly see what I did: loop from highest to lowest and add if the value is not equal or over our initial value. But this of course won't work in the example I provided (for value 287,5 since it would be much better to just choose 290 instead of two times 190).
It would be nice if I could get the algorithm to choose bigger numbers first and then use smaller ones.. but if you can provide a solution where smaller ones are used as a priority - that would be useful also.
I reckon I'd need a combination of sums to solve this - all possible combinations?!, but it would probably get messy. I don't mind a bit of recursion though...
Also, I think the second condition (else if) in my code is not really optimal - I'm sure there is a case where this wouldn't work properly and even go over the given value.
Any tips appreciated. Will work on my second solution until then :)
答案 0 :(得分:2)
如果优先考虑最大金额并且不能超过给定数量,则解决方案很简单。
计算最大金额适合的次数(这是除法的商)并推导出。
然后重复下一个最大的,依此类推。
1385: 1385/1000 = 1 => 385 remain
385: 385/ 540 = 0 => 385 remain
385: 385/ 290 = 1 => 95 remain
95: 95/ 190 = 0 => done.
答案 1 :(得分:1)
似乎这个问题类似于背包问题,这是NP难的,但如果初始权重具有足够大的公约数,则可以使用动态方法在可接受的时间内找到解决方案。
设{K, N, M, ...}
- 初始权重集S
- 我们需要向上舍入的数字。
生成一组权重W = {K, 2K, 3K,..., (|S/K| + 1)*K, N, 2N, ..., (|S/N| + 1)*N, M, ...}
,用于填充背包。对于重复权重,请根据偏好规则进行选择。
构建动态排序组合D
,其中项目(组合)按摘要权重排序,跟踪S
中等于或大于minExtra
的最小额外权重。换句话说,minExtra
是目前为止发现的最佳体重。
算法:
D = {};
minExtra = +inf
for each (e in W) { // take next element from W
for each (c in D) { // iterate over existing combinations in D
nw = c.weight + e.weight; // weight resulting of adding e
if (nw < minExtra) {
D.put(nw, c.add(e)) // assign to nw copy of c with e added
if (nw >= S)
minExtra = nw;
}
}
if (e.weight < minExtra) {
D.put(new C(e)) // put combination of only item e
if (e.weight >= S)
minExtra = e.weight
}
}
return D.get(minExtra)
如果首选更多项目的组合, D.put()
应接受现有重量的元素,否则将丢弃。
答案 2 :(得分:-1)
Given number: 2875
Expected result: 3020 (2x1000 + 1x540 + 1x290 + 1x190)
期待10 × 290 = 2900
更接近。如果这符合您的规则。
如果我是对的,这真的让我想起Knapsack problem,虽然它不是完全一样的。
编辑。如果你需要尽可能多的大数字,你正在寻找的算法看起来像这样:
-> fit as many 1000s as you can, save in "a"
-> take the smallest next number that makes the sum greater, save result in "b"
-> use the biggest number smaller than the "smaller next number" in previous step, add them in "a" until you can't fit them anymore
-> take the smallest next number that makes the sum greater, save result in "c"
-> etc.
-> return the closest value you end up with
如果它的表现不够好(结果不够好),那么你需要根据一些任意值来衡量这些数字,那就是完全一个背包问题。