题
我在寻找一种方法产生对象的组合订购单的属性。我不认为词便是我在寻找什么...我会试试举一个例子。让我们说有一个对象名单A、B、C、D所属性的价值观,我想到为了通过正在3,3,2,1.这给A3、B3,C2,D1对象。现在我想要产生2的对象,但他们需要订一个序的方式:
- A3B3
- A3C2
- B3C2
- A3D1
- B3D1
- C2D1
产生所有的组合和排序它们是不可接受的,因为真正的世界方案涉及大型集和数以百万计的组合。(设定的40时,8),我只需要组合上的某一阈值。
实际上我需要的计的组合上述阈值的分组通过一笔一定的属性,但我认为它远远更困难的事-所以,我会满足于发展的所有组合上述阈值和计他们。如果这是可能的。
编辑-我原来的问题不是非常精确的...我实际上不需要这些组合订购的,只是认为这将有助于隔离的组合上述阈值。为了更加精确,在上面的例子,给人一种阈值的5,我在找一个信息给定生产1组合总数的6(A3B3)和2和5(A3C2、B3C2)。我其实不需要该组合自己。
我找到的子集和问题,但是如果我理解正确的动态给予解决方案,它只会给你的信息是有一定总和,或不,不算的款项。
感谢
解决方案
实际上,我想你 做 想字典式的顺序,但下降,而不是上升。此外:
- 它不清楚我从你的说明,A,B,...D发挥任何作用于你的答案(除了可能作为容器值)。
- 我认为你的问题的例子是简单的"每个整数的至少5个,达到最大可能的共有两个值多少不同对从设{3,3,2,1}有总结这一整数?"
- 有趣的部分是早期救助,一旦有可能的解决方案可达到的(其余的可以实现的金额太小)。
我会后,代码样本之后。
这样的代码,我答应过,有几个发言如下:
public class Combos {
/* permanent state for instance */
private int values[];
private int length;
/* transient state during single "count" computation */
private int n;
private int limit;
private Tally<Integer> tally;
private int best[][]; // used for early-bail-out
private void initializeForCount(int n, int limit) {
this.n = n;
this.limit = limit;
best = new int[n+1][length+1];
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= length - i; ++j) {
best[i][j] = values[j] + best[i-1][j+1];
}
}
}
private void countAt(int left, int start, int sum) {
if (left == 0) {
tally.inc(sum);
} else {
for (
int i = start;
i <= length - left
&& limit <= sum + best[left][i]; // bail-out-check
++i
) {
countAt(left - 1, i + 1, sum + values[i]);
}
}
}
public Tally<Integer> count(int n, int limit) {
tally = new Tally<Integer>();
if (n <= length) {
initializeForCount(n, limit);
countAt(n, 0, 0);
}
return tally;
}
public Combos(int[] values) {
this.values = values;
this.length = values.length;
}
}
序言中的话:
这里使用了一个小帮手类称为货,只是分离的表格(包括初始化为以前从未见过的键)。我会把它放在最后。
保持这个简明扼要,我已经采取了一些捷径不良好做法"真正"代码:
- 这并不检查空值阵列,等等。
- 我认为值列已经按降序排列,所需要的早期救助的技术。(良好的生产码将包括分类。)
- 我把瞬时数据到实例的变量,而不是通过他们作为辩论中的私人方法,支持
count
.这使得这类非线的安全。
说明:
一个实例 Combos
是创造的(降序)列整数结合起来。的 value
阵列设置的每一次实例,但是,多个电话来 count
可以由不同的人口规模和限制。
的 count
方法中触发一(多)标准的递归穿越独特的组合 n
整数从 values
.的 limit
论点给人的下限数额的利息。
的 countAt
方法审查了组合的整数 values
.的 left
参数是多少整数仍作了 n
整数的总和, start
是的位置 values
从这搜索, sum
是部分的总和。
早期救助机制是基于计算 best
, 两维阵列,用于指定的"最好"的总和,可从一定状态。值 best[n][p]
是最大的一笔 n
值开始在位置 p
原始的 values
.
递归的 countAt
底出时正确的人口已经积累;这增加了目前的 sum
(的 n
值)的 tally
.如果 countAt
还没有见底,扫 values
从 start
-ing位置增加到目前的部分 sum
, ,只要:
- 足够位置留在
values
要实现指定的人口, - 的
best
(大)的小计剩余的大使limit
.
样品运行你的问题的数据:
int[] values = {3, 3, 2, 1};
Combos mine = new Combos(values);
Tally<Integer> tally = mine.count(2, 5);
for (int i = 5; i < 9; ++i) {
int n = tally.get(i);
if (0 < n) {
System.out.println("found " + tally.get(i) + " sums of " + i);
}
}
产生的结果规定:
found 2 sums of 5
found 1 sums of 6
这是讯号代码:
public static class Tally<T> {
private Map<T,Integer> tally = new HashMap<T,Integer>();
public Tally() {/* nothing */}
public void inc(T key) {
Integer value = tally.get(key);
if (value == null) {
value = Integer.valueOf(0);
}
tally.put(key, (value + 1));
}
public int get(T key) {
Integer result = tally.get(key);
return result == null ? 0 : result;
}
public Collection<T> keys() {
return tally.keySet();
}
}
其他提示
我写了一类来处理公共职能工作二项系数,这种类型的问题是你的问题落下。它将执行以下任务:
输出所有的K-索引中的一个很好的格式的任何N选择K文件。K-索引可以取代更具描述性的串或字母。这种方法使得解决此类问题相当微不足道的。
将K-索引到适当的索引条目的,在排序的二项系数表格。这种技术速度远远超过老年公布的技术依赖的迭代。它通过使用数学属性内在帕斯卡三角形。我的纸上谈到这一点。我相信我是第一个发现和发布这种技术,但我可能是错误的。
转换指数中的排二项系数表,以相应的K-索引。
使用 标记主人 方法二项计算系数,这是不太可能溢出和工作有更大的数字。
这类编写的。净C#和提供了一种方法来管理对象有关的问题(如果有)通过使用通用的名单。构造的这类需要bool值称为InitTable当真将创建一个通用的清单以保持对象,以管理。如果这个数值是错误的,那么它不会创建表。该表并不需要创建以来执行的4所述的方法。访问方法是提供给访问表。
有关试验的类显示如何使用类及其方法。已经广泛地测试了2起案件和没有任何已知的缺陷。
阅读有关此类和下载码,请参阅 Tablizing二项Coeffieicent.
看看这个问题在计算器: 算法返回的所有组合s
我也只是用一java代码下面产生的所有排列,但它可以很容易地用于生成的独特组合的指数。
public static <E> E[] permutation(E[] s, int num) {//s is the input elements array and num is the number which represents the permutation
int factorial = 1;
for(int i = 2; i < s.length; i++)
factorial *= i;//calculates the factorial of (s.length - 1)
if (num/s.length >= factorial)// Optional. if the number is not in the range of [0, s.length! - 1]
return null;
for(int i = 0; i < s.length - 1; i++){//go over the array
int tempi = (num / factorial) % (s.length - i);//calculates the next cell from the cells left (the cells in the range [i, s.length - 1])
E temp = s[i + tempi];//Temporarily saves the value of the cell needed to add to the permutation this time
for(int j = i + tempi; j > i; j--)//shift all elements to "cover" the "missing" cell
s[j] = s[j-1];
s[i] = temp;//put the chosen cell in the correct spot
factorial /= (s.length - (i + 1));//updates the factorial
}
return s;
}
我非常对不起(在所有这些澄清的意见)说,我不能找到有效解决这个问题。我试图过去一小时内没有结果。
原因(我认为),这个问题是非常相似的问题,如在旅行推销员的问题。直到除非你尝试所有的组合,有没有办法知道哪些属性将增加最多的阈值。
似乎没有聪明的技巧,就可以解决这类问题。
仍然有许多优化,你可以做到的实际代码。
尽量分类的数据的属性。你可能可以避免处理一些值的列表当你找到一个更高的价值无法满足的阈值(因此,所有低价值可能被淘汰).
如果你使用C#还有一个相当不错的仿制药库 在这里,.注意到虽然会产生一些排列是不是在字典式了
这里有一个递归的方法 计 这些数目的子集:我们定义的一个函数 count(minIndex,numElements,minSum)
这回的数目的子集的大小 numElements
他总是至少 minSum
, 的、含有元素与指数 minIndex
或者更大。
如在问题的声明,我们我们要素的顺序,例如[3,3,2,1],并呼吁的第一个指数为零,总人数N.元素我们假定所有的元素都是非负。找到所有2-亚群的总和至少5个,我们呼 count(0,2,5)
.
代码样本 (Java):
int count(int minIndex, int numElements, int minSum)
{
int total = 0;
if (numElements == 1)
{
// just count number of elements >= minSum
for (int i = minIndex; i <= N-1; i++)
if (a[i] >= minSum) total++; else break;
}
else
{
if (minSum <= 0)
{
// any subset will do (n-choose-k of them)
if (numElements <= (N-minIndex))
total = nchoosek(N-minIndex, numElements);
}
else
{
// add element a[i] to the set, and then consider the count
// for all elements to its right
for (int i = minIndex; i <= (N-numElements); i++)
total += count(i+1, numElements-1, minSum-a[i]);
}
}
return total;
}
顺便说一句,我已经运行上述一系列的40件和大小-8子和一贯地得到了回来的结果在不到一秒钟。