用于查找两个有序整数列表的乘积之和的目标和的算法

时间:2015-12-21 21:44:04

标签: java algorithm

我正在尝试编写一个脚本来解析文本形式的转换不良的表,这些表源自解析的pdf:s到csv:s。基本上标题是木板的长度,数据是木板的数量,最后给出了行中所有木板的总长度。

简化示例

1,0   2,0   3,0   4,0 5,0   total M
1      3    2     1         17,0

由于布局变化很大,而且我不需要保证正确性,我认为有一个很好的机会,只需要尝试所有有效的木板组合时间长度加在一起,看看哪些是正确的应该工作够了。

作为一个概念证明,我想写一个简单的程序,它需要两个整数列表,并查找所有有效的产品总和,看看我没有得到组合的噩梦。

这个玩具问题的规则是。

两个整数列表,第一个[1..14],第二个小整数(<1000)和1到14个成员。称他们为长度和numPlanks

目标总和,通过将numPlanks的所有成员的产品与恰好一个长度成员相加而没有两个numPlanks成员可以共享一个长度。搜索所有此类组合并打印与目标匹配的组合。

此外,两个名单的成员都是订购的。如果numPlanks的第一个元素与lenghts的第二个元素相乘,则numPlanks的其他成员不能与第一个length元素相乘。

示例,伪代码

lengths = [1, 2, 3, 4]
numPlanks = [10, 20]
target = 110
然后程序将检查10 + 40,10 + 60,10 + 80,20 + 60,20 + 80,30 + 80以查看哪些加起来并最终打印出像&#34; 10 * 30 + 20 * 40 = 110&#34;。

我一直在尝试构建解决方案,但我只是因为能够想到嵌套与numPlanks中的成员一样多的循环而感到难过。这看起来很可怕。

该程序是用java编写的,所以如果有人想指出任何特定于语言的东西,我会非常感激,其他任何东西当然都很棒。

编辑:使用笔和纸绘制草图似乎比较的数量与Pascal的三角形相关。例如,对于具有两个成员的长度和具有0到2个成员的numPlacks,对于numPlanks中的0,1和2成员,比较的数量分别为1,2,1。

鉴于我知道我在实际问题中有14个成员长度,而在numPlanks中有1到14个成员,这将对应于1716个比较的最坏情况。这看起来还不错。

2 个答案:

答案 0 :(得分:0)

简短的回答:除非我误解了你的问题,否则你对计算次数的估计是绝对乐观的。

假设lengths中有14个元素,numPlanks中有14个元素。由于每个长度和每个numPlank只能使用一次(如果我理解正确的话),那么你基本上有14 * 14 = 196个可能的术语,你需要找到它们的一些组合来累加你的目标。

假设您首先猜测解决方案包含特定长度和特定numPlanks。这意味着您可以跨越13个与您的猜测具有相同numPlanks的其他术语和其他13个与您的猜测具有相同长度的术语。当然,你可以越过你猜的那个词。这仍然留下169个术语,试图添加到那个猜测。

所以,你选择。现在你像以前一样交出12 + 13个术语,因为它们与你在第二学期的猜测共享一个值。现在,你已经剩下144个条款......等等。

所以,只是为了得到所有可能的3个术语的猜测,你必须看看196 * 169 * 144 = 470万种可能性。

这是一些生成解决方案的Java代码。此版本在每个数组中有14个元素。它在64000次比较后找到了一个解决方案(远远高于你最差的估计)。如果你给它一些不可解决的东西(例如,将所有数字长度整除10并给它一个2051的目标),那么去喝杯咖啡等待宇宙结束......

public class Tester {

    static int numComparisons = 0;

    public static class Term {
        final int length;
        final int numPlanks;

        public Term(final int length, final int numPlanks) {
            this.length = length;
            this.numPlanks = numPlanks;
        }

        @Override
        public String toString() {
            return "(" + this.length + "*" + this.numPlanks + ")";
        }
    }

    public static List<Term> getTerms(int target, List<Integer> lengthsList,
            List<Integer> numPlanksList, List<Term> currentTermList) {
        // System.out.println("... " + target + ", " + lengthsList + ", " +
        // numPlanksList + ", " + currentTermList);
        lengthsLoop: for (int l : lengthsList) {
            numPlanksLoop: for (int n : numPlanksList) {
                numComparisons++;
                if (numComparisons % 100 == 0) {
                    System.out.println("... numComparisons = " + numComparisons
                            + " .... " + currentTermList);
                }
                if ((l * n) > target) {
                    continue lengthsLoop;
                } else if (l * n == target) {
                    final List<Term> newCurrentTermList = new ArrayList<Term>(
                            currentTermList);
                    newCurrentTermList.add(new Term(l, n));
                    return newCurrentTermList;
                } else {
                    final int newTarget = target - (l * n);
                    final List<Integer> newLengthsList = new ArrayList<Integer>(
                            lengthsList);
                    newLengthsList.remove((Integer) l);
                    final List<Integer> newNumPlanksList = new ArrayList<Integer>(
                            numPlanksList);
                    newNumPlanksList.remove((Integer) n);
                    final List<Term> newCurrentTermList = new ArrayList<Term>(
                            currentTermList);
                    newCurrentTermList.add(new Term(l, n));
                    final List<Term> answer = getTerms(newTarget,
                            newLengthsList, newNumPlanksList,
                            newCurrentTermList);
                    if (answer.size() > 0) {
                        return answer;
                    }
                }
            }
        }

        // System.out.println("Over!");
        return new ArrayList<Term>();

    }

    public static void main(String[] args) {

        List<Integer> lengthsList = new ArrayList<Integer>(Arrays.asList(1, 2,
                3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14));
        Collections.sort(lengthsList, Collections.reverseOrder());
        List<Integer> numPlanksList = new ArrayList<Integer>(Arrays.asList(1,
                20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140));
        Collections.sort(numPlanksList, Collections.reverseOrder());
        int target = 2051;

        final List<Term> finalAnswer = getTerms(target, lengthsList,
                numPlanksList, new ArrayList<Term>());
        if (finalAnswer.size() > 0) {
            System.out.println("Final Answer:");
            System.out.println("=============");
            for (Term t : finalAnswer) {
                System.out.println(t.length + "*" + t.numPlanks);
            }
        } else {
            System.out.println("No solution");
        }
        System.out.println("numComparisons = " + numComparisons);

    }
}

答案 1 :(得分:0)

因为整数数组是有序的,所以这应该是一个快速的解决方案。

使用

进行测试
    int[] lengths = { 1, 2, 3, 4 };
    int[] plankCount = { 10, 20 };
    int totalPlankLength = 110;

我得到了以下结果:

    [10 x 3, 20 x 4]

使用

进行测试
    int[] lengths = { 1, 2, 3, 4, 5, 6, 7 };
    int[] plankCount = { 10, 20, 30 };
    int totalPlankLength = 280;

我得到了以下结果

    [10 x 1, 20 x 3, 30 x 7]
    [10 x 2, 20 x 4, 30 x 6]

感谢greybeard的评论。在一个组成的例子中,可能不止一个答案适合。使用真实数据,它不太可能,但仍有可能。

我使用二进制文件来帮助我创建一组木板计数乘以长度的可能性。除了它解决了问题之外,二进制没有什么神奇之处。

让我用更简单的例子来说明。我们有以下输入:

    int[] lengths = { 1, 2, 3, 4 };
    int[] plankCount = { 10, 20 };
    int totalPlankLength = 110;

因此,我们需要一种方法来获得将木板计数与长度相乘的所有可能方法。

首先,我通过计算长度2的长度来计算可能性的数量。在这个例子中,我们计算2到4的幂,或16。

由于我们使用的是int,因此List的最大长度为30.如果你想要更长的List,你必须将int转换为long。

我们不需要查看15和0之间的所有二进制数。我们只需要查看一次使用plankCount长度的二进制数。我们以相反的顺序查看二进制数。

12 1100
10 1010
 9 1001
 6 0110
 5 0101
 3 0011

左边的十进制数无关紧要。右边的位模式是重要的。这组位模式显示了可以将plankCount乘以长度的方式的数量。

因此,我们将使用两个木板计数执行6次乘法,总共12次乘法。这很快就会发生。

我进行乘法并对每个二进制模式的乘积求和,以查看总和是否等于总板长。如果是这样,我会写出那些乘法。

这是更正后的代码。尝试使用14种长度,看它是否足够快以满足您的需求。

package com.ggl.testing;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BoardLength {

    public static void main(String[] args) {
        BoardLength boardLength = new BoardLength();
        int[] lengths = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        int[] plankCount = { 10, 20, 30 };
        int totalPlankLength = 360;
        List<List<PlankLength>> plankLength = boardLength.calculatePlankLength(
                lengths, plankCount, totalPlankLength);
        displayResults(plankLength);
    }

    private static void displayResults(List<List<PlankLength>> plankLength) {
        if (plankLength.size() <= 0) {
            System.out.println("[]");
        } else {
            for (List<PlankLength> list : plankLength) {
                System.out.println(Arrays.toString(list.toArray()));
            }
        }
    }

    public List<List<PlankLength>> calculatePlankLength(int[] lengths,
            int[] plankCount, int totalPlankLength) {
        List<List<PlankLength>> plankLength = new ArrayList<>();
        String formatString = "%" + lengths.length + "s";

        int maximum = (int) Math.round(Math.pow(2D, (double) lengths.length));
        for (int index = maximum - 1; index >= 0; index--) {
            int bitCount = Integer.bitCount(index);
            if (bitCount == plankCount.length) {
                String bitString = String.format(formatString,
                        Integer.toBinaryString(index)).replace(' ', '0');
                calculateTotalPlankLength(lengths, plankCount,
                        totalPlankLength, plankLength, bitString);
            }
        }

        return plankLength;
    }

    private void calculateTotalPlankLength(int[] lengths, int[] plankCount,
            int totalPlankLength, List<List<PlankLength>> plankLength,
            String bitString) {
        List<PlankLength> tempList = new ArrayList<>();
        int plankIndex = 0;
        int sum = 0;
        for (int bitIndex = 0; bitIndex < bitString.length(); bitIndex++) {
            if (bitString.charAt(bitIndex) == '1') {
                PlankLength pl = new PlankLength(lengths[bitIndex],
                        plankCount[plankIndex++]);
                sum += pl.getPlankLength();
                tempList.add(pl);
            }
        }

        if (sum == totalPlankLength) {
            plankLength.add(tempList);
        }
    }

    public class PlankLength {
        private final int length;
        private final int plankCount;

        public PlankLength(int length, int plankCount) {
            this.length = length;
            this.plankCount = plankCount;
        }

        public int getLength() {
            return length;
        }

        public int getPlankCount() {
            return plankCount;
        }

        public int getPlankLength() {
            return length * plankCount;
        }

        @Override
        public String toString() {
            return "" + plankCount + " x " + length;
        }

    }

}