【发布时间】:2020-02-19 10:25:21
【问题描述】:
假设我有几个排序的正数列表,例如:
double[] a1 = new double[]{0.70, 0.20, 0.10};
double[] a2 = new double[]{0.80, 0.10, 0.05, 0.05};
double[] a3 = new double[]{0.60, 0.15, 0.14, 0.10, 0.01};
我想按条目乘积递减的顺序遍历这些数组的笛卡尔积,如下所示:
0000: Combo[product=3.36e-01, vals=[0.70, 0.80, 0.60], indexes=[0, 0, 0]]
0001: Combo[product=9.60e-02, vals=[0.20, 0.80, 0.60], indexes=[1, 0, 0]]
0002: Combo[product=8.40e-02, vals=[0.70, 0.80, 0.15], indexes=[0, 0, 1]]
0003: Combo[product=7.84e-02, vals=[0.70, 0.80, 0.14], indexes=[0, 0, 2]]
0004: Combo[product=5.60e-02, vals=[0.70, 0.80, 0.10], indexes=[0, 0, 3]]
0005: Combo[product=4.80e-02, vals=[0.10, 0.80, 0.60], indexes=[2, 0, 0]]
...
在上面的示例中,第一个条目很明显(因为数组已排序),它是第一个值的组合:[0.70, 0.80, 0.60] 与产品 0.70*0.80*0.60 = 3.36e-01 和数组中对应的值索引 a1, a2, a3 是 [0, 0, 0] .现在第二个条目不太明显,我们应该将0.70 更改为0.20 吗?还是0.60 到0.15?还是0.80 到0.10?第二个应该是[0.20, 0.80, 0.60],产品9.60e-02,索引[1, 0, 0]。
这是一个用 Java 生成/打印它们的程序:https://repl.it/repls/FilthyGreatRotation(所有逻辑都在 printWholeCartesianProduct() 方法中)
该程序按字典顺序生成它们,然后按产品对整个集合进行排序。
问题:有没有一种简单的方法可以首先以正确的顺序实际生成组合?
原因:首先我没有列表,只有一些排序的数字集合的迭代器。可能很长,长度未知,但已知每个迭代器中的数字都已排序。
要使用的 MVCE(与上面的 https://repl.it 链接相同):
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<List<Double>> data = createData();
printWholeCartesianProduct(data);
}
public static List<List<Double>> createData() {
double[] a1 = new double[]{0.70, 0.20, 0.10};
double[] a2 = new double[]{0.80, 0.10, 0.05, 0.05};
double[] a3 = new double[]{0.60, 0.15, 0.14, 0.10, 0.01};
return createData(a1, a2, a3);
}
public static void printWholeCartesianProduct(List<List<Double>> data) {
final DecimalFormat df = new DecimalFormat("0.00");
// print input data
String matrix = data.stream()
.map(l -> l.stream().map(df::format).collect(Collectors.joining(", ")))
.map(row -> "[" + row + "]")
.collect(Collectors.joining("\n"));
System.out.println("Input data:\n" + matrix);
// collect combos as they are generated
final List<Combo> combos = new ArrayList<>();
Consumer<int[]> callback = indexes -> {
double[] v = new double[indexes.length];
double prod = 1;
for (int i = 0; i < indexes.length; i++) {
List<Double> col = data.get(i);
int index = indexes[i];
v[i] = col.get(index);
prod *= v[i];
}
combos.add(new Combo(prod, v, indexes.clone()));
};
// generate combos
int[] c = new int[data.size()];
int ptr = c.length - 1;
while (ptr >= 0) {
callback.accept(c);
c[ptr]++; // increment
if (c[ptr] == data.get(ptr).size()) { // carry
do {
ptr--;
} while(ptr >= 0 && c[ptr] == data.get(ptr).size() - 1);
if (ptr < 0) {
break;
}
c[ptr]++;
// zero out
while (++ptr <= c.length - 1) {
c[ptr] = 0;
}
ptr = c.length - 1;
}
}
// cheating - sort after generation and print result
combos.sort((o1, o2) -> Double.compare(o2.product, o1.product));
StringBuilder sb = new StringBuilder();
double totalP = 0;
for (int i = 0; i < combos.size(); i++) {
sb.append(String.format("%04d: ", i)).append(combos.get(i)).append("\n");
totalP += combos.get(i).product;
}
System.out.printf("Cartesian product in descending product (total p=%.3e):\n%s", totalP, sb.toString());
}
public static List<Double> asList(double[] a) {
return Arrays.stream(a).boxed().collect(Collectors.toList());
}
public static List<List<Double>> createData(double[]... arrays) {
final List<List<Double>> vals = new ArrayList<>();
Arrays.stream(arrays).forEachOrdered(a -> vals.add(asList(a)));
return vals;
}
static class Combo {
final double product;
final double[] vals;
final int[] indexes;
Combo(double product, double[] vals, int[] indexes) {
this.product = product;
this.vals = vals;
this.indexes = indexes;
}
@Override
public String toString() {
return new StringJoiner(", ", Combo.class.getSimpleName() + "[", "]")
.add("product=" + String.format("%.2e", product))
.add("vals=[" + Arrays.stream(vals).boxed().map(v -> String.format("%.2f", v)).collect(
Collectors.joining(", ")) + "]")
.add("indexes=" + Arrays.toString(indexes))
.toString();
}
}
}
【问题讨论】:
-
二次进入选择
[0.20, 0.80, 0.60]的依据是什么? -
预期结果的顺序是什么?它似乎是按产品价值降序排列的。
-
我相信你需要将你的数字限制为非负数。如果允许负数,则没有算法可以做到,因为两个大(绝对值大)负数的乘积会产生一个大正数。而且您无法在列表的末尾预见“大”负数。
-
@caisil 这正是要求中所说的,降低了产品价值
-
请注意,既然每个人都是积极的,你可能想看看stackoverflow.com/questions/49417578/…
标签: java algorithm iteration cartesian-product