受限单目标优化

huangapple go评论82阅读模式
英文:

Constrained Single-Objective Optimization

问题

Introduction

我需要将一个填充有某种类型(以水桶为例)的数组分成两个值设置(在本例中为重量和体积),同时保持重量总和的差异最小(优先)和体积总和的差异小于1000(必需)。这不需要是一个完整的遗传算法或类似的东西,但它应该比我目前拥有的更好...

Current Implementation

由于不知道如何更好地做到这一点,我首先将数组分成两个相同长度的数组(数组可以填充不均匀的项目),用两个值都为0的项目替换可能的空位。两边不需要有相同数量的项目,我只是不知道如何处理。

在分配完这些之后,我尝试通过以下方式进行优化:

func (main *Main) Optimize() {
    for {
        difference := main.Difference(WEIGHT)

        for i := 0; i < len(main.left); i++ {
            for j := 0; j < len(main.right); j++ {
                if main.DifferenceAfter(i, j, WEIGHT) < main.Difference(WEIGHT) {
                    main.left[i], main.right[j] = main.right[j], main.left[i]
                }
            }
        }
        
        if difference == main.Difference(WEIGHT) {
            break
        }
    }

    for main.Difference(CAPACITY) > 1000 {
        leftIndex := 0
        rightIndex := 0
        liters := 0
        weight := 100

        for i := 0; i < len(main.left); i++ {
            for j := 0; j < len(main.right); j++ {
                if main.DifferenceAfter(i, j, CAPACITY) < main.Difference(CAPACITY) {
                    newLiters := main.Difference(CAPACITY) - main.DifferenceAfter(i, j, CAPACITY)
                    newWeight := main.Difference(WEIGHT) - main.DifferenceAfter(i, j, WEIGHT)
                    
                    if newLiters > liters && newWeight <= weight || newLiters == liters && newWeight < weight {
                        leftIndex = i
                        rightIndex = j
                        liters = newLiters
                        weight = newWeight
                    }
                }
            }
        }

        main.left[leftIndex], main.right[rightIndex] = main.right[rightIndex], main.left[leftIndex]
    }
}

Functions:

main.Difference(const) 计算两边之间的绝对差异,常量作为参数决定要计算差异的值

main.DifferenceAfter(i, j, const) 模拟交换两个桶,i为左边的桶,j为右边的桶,并计算结果的绝对差异,常量再次确定要检查的值

Explanation:

基本上,这首先通过优化重量来开始,这是第一个for循环所做的。在每次迭代中,它尝试可以交换的每个桶的所有可能组合,如果交换后的差异小于当前差异(导致更好的分配),则进行交换。如果重量不再改变,它就会跳出for循环。虽然不完美,但这个方法效果相当不错,我认为这对我想要实现的目标来说是可以接受的。

然后,它应该根据体积优化分配,使总差异小于1000。在这里,我试图更加小心,在进行交换之前运行搜索以找到最佳组合。因此,它搜索导致容量变化最大的桶交换,并且还应该在这之间寻找权衡,尽管我看到的缺陷是第一个尝试的桶组合将设置升升和重量变量,导致下一个可能的组合减少了很大的数量。

Conclusion

我认为我需要在这里加入一些更多的数学知识,但是我在这里真的陷入了困境,不知道如何继续下去,所以我希望能从你这里得到一些帮助,任何可以帮助我的人都是受欢迎的。

英文:

Introduction

I need to split an array filled with a certain type (let's take water buckets for example) with two values set (in this case weight and volume), while keeping the difference between the total of the weight to a minimum (preferred) and the difference between the total of the volumes less than 1000 (required). This doesn't need to be a full-fetched genetic algorithm or something similar, but it should be better than what I currently have...

Current Implementation

Due to not knowing how to do it better, I started by splitting the array in two same-length arrays (the array can be filled with an uneven number of items), replacing a possibly void spot with an item with both values being 0. The sides don't need to have the same amount of items, I just didn't knew how to handle it otherwise.

After having these distributed, I'm trying to optimize them like this:

func (main *Main) Optimize() {
    for {
        difference := main.Difference(WEIGHT)

        for i := 0; i &lt; len(main.left); i++ {
            for j := 0; j &lt; len(main.right); j++ {
                if main.DifferenceAfter(i, j, WEIGHT) &lt; main.Difference(WEIGHT) {
                    main.left[i], main.right[j] = main.right[j], main.left[i]
                }
            }
        }
        
        if difference == main.Difference(WEIGHT) {
            break
        }
    }

    for main.Difference(CAPACITY) &gt; 1000 {
        leftIndex := 0
        rightIndex := 0
        liters := 0
        weight := 100

        for i := 0; i &lt; len(main.left); i++ {
            for j := 0; j &lt; len(main.right); j++ {
                if main.DifferenceAfter(i, j, CAPACITY) &lt; main.Difference(CAPACITY) {
                    newLiters := main.Difference(CAPACITY) - main.DifferenceAfter(i, j, CAPACITY)
                    newWeight := main.Difference(WEIGHT) - main.DifferenceAfter(i, j, WEIGHT)
                    
                    if newLiters &gt; liters &amp;&amp; newWeight &lt;= weight || newLiters == liters &amp;&amp; newWeight &lt; weight {
                        leftIndex = i
                        rightIndex = j
                        liters = newLiters
                        weight = newWeight
                    }
                }
            }
        }

        main.left[leftIndex], main.right[rightIndex] = main.right[rightIndex], main.left[leftIndex]
    }
}

Functions:

main.Difference(const) calculates the absolute difference between the two sides, the constant taken as an argument decides the value to calculate the difference for

main.DifferenceAfter(i, j, const) simulates a swap between the two buckets, i being the left one and j being the right one, and calculates the resulting absolute difference then, the constant again determines the value to check

Explanation:

Basically this starts by optimizing the weight, which is what the first for-loop does. On every iteration, it tries every possible combination of buckets that can be switched and if the difference after that is less than the current difference (resulting in better distribution) it switches them. If the weight doesn't change anymore, it breaks out of the for-loop. While not perfect, this works quite well, and I consider this acceptable for what I'm trying to accomplish.

Then it's supposed to optimize the distribution based on the volume, so the total difference is less than 1000. Here I tried to be more careful and search for the best combination in a run before switching it. Thus it searches for the bucket switch resulting in the biggest capacity change and is also supposed to search for a tradeoff between this, though I see the flaw that the first bucket combination tried will set the liters and weight variables, resulting in the next possible combinations being reduced by a big a amount.

Conclusion

I think I need to include some more math here, but I'm honestly stuck here and don't know how to continue here, so I'd like to get some help from you, basically that can help me here is welcome.

答案1

得分: 2

如前所述,您的问题实际上是一个带有体积差约束的约束优化问题。

从数学上讲,这将是最小化体积差的问题,在体积差小于1000的约束下。将其表达为线性优化问题的最简单方法是:

min weights . x
    subject to  volumes . x &lt; 1000.0
                for all i, x[i] = +1 or -1

其中a . b是向量点积。解决了这个问题后,所有x = +1的索引对应于您的第一个数组,所有x = -1的索引对应于您的第二个数组。

不幸的是,0-1整数规划被认为是NP-hard的。解决它的最简单方法是对空间进行穷举式的暴力搜索,但这需要测试所有可能的向量x(其中n是原始weightsvolumes向量的长度),这可能很快变得难以处理。关于这个主题有很多文献,涉及更高效的算法,但它们通常高度特定于特定的问题和/或约束集。您可以搜索“线性整数规划”来了解有关此主题的研究成果。

我认为最简单的方法可能是执行基于启发式的暴力搜索,在搜索树超出体积约束时提前剪枝,并保持接近约束(一般规则是,线性优化问题的解在可行空间的边缘上)。

以下是您可能想阅读的一些关于这种优化的文章:

如果您对优化文章或数学不熟悉,维基百科文章提供了一个很好的介绍,但大多数关于这个主题的文章很快就会展示一些(伪)代码,您可以立即适应。

如果您的n很大,我认为在某个时候您将不得不在解决方案的优化程度和计算速度之间做出权衡。您的解决方案可能是次优的,但比穷举搜索快得多。根据问题的确切配置,可能存在更好的权衡。

英文:

As previously said, your problem is actually a constrained optimisation problem with a constraint on your difference of volumes.

Mathematically, this would be minimise the difference of volumes under constraint that the difference of volumes is less than 1000. The simplest way to express it as a linear optimisation problem would be:

min weights . x
    subject to  volumes . x &lt; 1000.0
                for all i, x[i] = +1 or -1

Where a . b is the vector dot product. Once this problem is solved, all indices where x = +1 correspond to your first array, all indices where x = -1 correspond to your second array.

Unfortunately, 0-1 integer programming is known to be NP-hard. The simplest way of solving it is to perform exhaustive brute force exploring of the space, but it requires testing all 2^n possible vectors x (where n is the length of your original weights and volumes vectors), which can quickly get out of hands. There is a lot of literature on this topic, with more efficient algorithms, but they are often highly specific to a particular set of problems and/or constraints. You can google "linear integer programming" to see what has been done on this topic.

I think the simplest might be to perform a heuristic-based brute force search, where you prune your search tree early when it would get you out of your volume constraint, and stay close to your constraint (as a general rule, the solution of linear optimisation problems are on the edge of the feasible space).

Here are a couple of articles you might want to read on this kind of optimisations:

If you are not familiar with optimisation articles or math in general, the wikipedia articles provides a good introduction, but most articles on this topic quickly show some (pseudo)code you can adapt right away.

If your n is large, I think at some point you will have to make a trade off between how optimal your solution is and how fast it can be computed. Your solution is probably suboptimal, but it is much faster than the exhaustive search. There might be a better trade off, depending on the exact configuration of your problem.

答案2

得分: 1

在你的情况下,重量差异是客观的,而体积差异只是一个约束条件,这意味着你正在寻找优化重量差异属性(尽可能小)并满足体积差异属性(总和<1000)的解决方案。在这种情况下,这是一个单目标约束优化问题。

而如果你对多目标优化感兴趣,也许你想看一下帕累托前沿的概念:http://en.wikipedia.org/wiki/Pareto_efficiency。它适用于保留在不同目标上具有优势的多个良好解决方案,即不失去多样性。

英文:

It seems that in your case, difference of weight is objective, while difference of volume is just a constraint, which means that you are seeking for solutions that optimize difference of weight attribute (as small as possible), and satisfy the condition on difference of volume attribute (total < 1000). In this case, it's a single objective constrained optimization problem.

Whereas, if you are interested in multi-objective optimization, maybe you wanna look at the concept of Pareto Frontier: http://en.wikipedia.org/wiki/Pareto_efficiency . It's good for keeping multiple good solutions with advantages in different objective, i.e., not losing diversity.

huangapple
  • 本文由 发表于 2012年11月2日 02:46:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/13183547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定