我正在尝试编写一个脚本来解析文本形式的转换不良的表,这些表源自解析的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个比较的最坏情况。这看起来还不错。
答案 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;
}
}
}