我正在尝试找到一种算法来识别结果的有序组合,如下所示:
例如,假设N = 3,结果排名如下:
Contest 1 Win = 1
Contest 1 Tie = 4
Contest 1 Loss = 7
Contest 2 Win = 2
Contest 2 Tie = 5
Contest 2 Loss = 8
Contest 3 Win = 3
Contest 3 Tie = 6
Contest 3 Loss = 9
鉴于这些排名,组合应按如下顺序排列:
Contest 1 Win (1), Contest 2 Win (2), Contest 3 Win (3)
Contest 1 Win (1), Contest 2 Win (2), Contest 3 Tie (6)
Contest 1 Win (1), Contest 2 Win (2), Contest 3 Loss (9)
Contest 1 Win (1), Contest 2 Tie (5), Contest 3 Win (3)
Contest 1 Win (1), Contest 2 Loss (8), Contest 3 Win (3)
Contest 1 Win (1), Contest 2 Tie (5), Contest 3 Tie (6)
Contest 1 Win (1), Contest 2 Tie (5), Contest 3 Loss (9)
Contest 1 Win (1), Contest 2 Loss (8), Contest 3 Win (6)
Contest 1 Win (1), Contest 2 Loss (8), Contest 3 Loss (9)
Contest 1 Tie (4), Contest 2 Win (2), Contest 3 Win (3)
Contest 1 Loss (7), Contest 2 Win (2), Contest 3 Win (3)
Contest 1 Tie (4), Contest 2 Win (2), Contest 3 Tie (6)
Contest 1 Tie (4), Contest 2 Win (2), Contest 3 Loss (9)
Contest 1 Loss (7), Contest 2 Win (2), Contest 3 Tie (6)
Contest 1 Loss (7), Contest 2 Win (2), Contest 3 Loss (9)
Contest 1 Tie (4), Contest 2 Tie (5), Contest 3 Win (3)
Contest 1 Tie (4), Contest 2 Loss (8), Contest 3 Win (3)
Contest 1 Loss (7), Contest 2 Tie (5), Contest 3 Win (3)
Contest 1 Loss (7), Contest 2 Loss (8), Contest 3 Win (3)
Contest 1 Tie (4), Contest 2 Tie (5), Contest 3 Tie (6)
Contest 1 Tie (4), Contest 2 Tie (5), Contest 3 Loss (9)
Contest 1 Tie (4), Contest 2 Loss (8), Contest 3 Tie (6)
Contest 1 Tie (4), Contest 2 Loss (8), Contest 3 Loss (9)
Contest 1 Loss (7), Contest 2 Tie (5), Contest 3 Tie (6)
Contest 1 Loss (7), Contest 2 Tie (5), Contest 3 Loss (9)
Contest 1 Loss (7), Contest 2 Loss (8), Contest 3 Tie (6)
Contest 1 Loss (7), Contest 2 Loss (8), Contest 3 Loss (9)
我正在寻找一种方法来生成这些组合以便任意大的N值,尽管我不希望得到所有组合。例如,在N = 256和总共3 ^ 256种组合的情况下,我希望找到前500种组合。
答案 0 :(得分:4)
此算法似乎有效。 Python中的实现如下。
基本上我接受输入,然后按给定结果的值对其进行排序:
Contest 1 Win = 1
Contest 2 Win = 2
Contest 3 Win = 3
Contest 1 Tie = 4
Contest 2 Tie = 5
Contest 3 Tie = 6
Contest 1 Loss = 7
Contest 2 Loss = 8
Contest 3 Loss = 9
我称之为排序。然后我生成一个空的结果列表:
[None, None, None]
递归算法非常简单,如下所示:
这是代码。还有一个额外的技巧可以避免重复,如果我们只是填写说排序#6,我们只会使用排序#7,8和9。
#rankings as tuple of (winval, tieval, lossval) for each
#contest
rankings = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
#first sort the rankings by their values into
#list of (contestnum, w/t/l, value)
orderings = []
for i, (w, t, l) in enumerate(rankings):
orderings.append((i, 'w', w))
orderings.append((i, 't', t))
orderings.append((i, 'l', l))
orderings.sort(key=lambda (i,res,val): val)
#now, find solution recursively as follows:
#- if list is full then print result & return
#- else, iterate thru the rankings & recur for each unused slot
def solve(orderings, slots, used_orderings, first_ordering):
if all(slot is not None for slot in slots):
yield slots
return
i = first_ordering
while i < len(orderings):
slot, result, value = orderings[i]
if used_orderings[i]:
i += 1
continue
if slots[slot] is not None:
i += 1
continue
slots[slot] = (result, value)
used_orderings[i] = True
for solution in solve(orderings, slots, used_orderings, i):
yield solution
#backtrack
slots[slot] = None
used_orderings[i] = False
i += 1
#print the first 40 solutions
num_solutions = 0
for solution in solve(orderings, [None]*len(rankings), [False]*len(orderings), 0):
print "Solution #%d: %s" % (num_solutions+1, solution)
num_solutions += 1
if num_solutions >= 40:
break
以下是针对给定输入打印的结果,其匹配问题:
Solution #1: [('w', 1), ('w', 2), ('w', 3)]
Solution #2: [('w', 1), ('w', 2), ('t', 6)]
Solution #3: [('w', 1), ('w', 2), ('l', 9)]
Solution #4: [('w', 1), ('t', 5), ('w', 3)]
Solution #5: [('w', 1), ('l', 8), ('w', 3)]
Solution #6: [('w', 1), ('t', 5), ('t', 6)]
Solution #7: [('w', 1), ('t', 5), ('l', 9)]
Solution #8: [('w', 1), ('l', 8), ('t', 6)]
Solution #9: [('w', 1), ('l', 8), ('l', 9)]
Solution #10: [('t', 4), ('w', 2), ('w', 3)]
Solution #11: [('l', 7), ('w', 2), ('w', 3)]
Solution #12: [('t', 4), ('w', 2), ('t', 6)]
Solution #13: [('t', 4), ('w', 2), ('l', 9)]
Solution #14: [('l', 7), ('w', 2), ('t', 6)]
Solution #15: [('l', 7), ('w', 2), ('l', 9)]
Solution #16: [('t', 4), ('t', 5), ('w', 3)]
Solution #17: [('t', 4), ('l', 8), ('w', 3)]
Solution #18: [('l', 7), ('t', 5), ('w', 3)]
Solution #19: [('l', 7), ('l', 8), ('w', 3)]
Solution #20: [('t', 4), ('t', 5), ('t', 6)]
Solution #21: [('t', 4), ('t', 5), ('l', 9)]
Solution #22: [('t', 4), ('l', 8), ('t', 6)]
Solution #23: [('t', 4), ('l', 8), ('l', 9)]
Solution #24: [('l', 7), ('t', 5), ('t', 6)]
Solution #25: [('l', 7), ('t', 5), ('l', 9)]
Solution #26: [('l', 7), ('l', 8), ('t', 6)]
Solution #27: [('l', 7), ('l', 8), ('l', 9)]
如果我为256场比赛随机生成一组排名,它似乎会立即运行。
答案 1 :(得分:1)
首先,让我们重新解释一下这个问题,以便抽象细节,并确保我们正在谈论同样的问题。
有3 ^ N个长度为N的元组。每个元组的组件a_i(a_1,a_2,...,a_N)是1到3N之间的不同整数,对于固定的i,a_i只能取值在基数S_i中,基数为3.对于[1,3N]中的每个值,元组中只有一个位置可以假定该值。
现在,通过排序(S)表示以整数的自然顺序对元组S的组件进行排序所产生的元组。我们说元组S小于元组T如果排序(S)小于按字典顺序排序(T)。
问题在于在现有的3 ^ N个中找到给定顺序中的前M个元组,其中M <&lt; 3 ^ N
我看到的解决方案原则基本上是修剪回溯。
以严格的方式修剪搜索空间,计算3的最大功率不大于M.假设这个幂是H.我们有3 ^(H + 1)&gt; M> = 3 ^ H.此时,您知道您的M元组位于一个集合中,其中(N-H-1)元组组件采用其最小可能值。可以按如下方式找到并修复这些组件:首先,获取值为1的组件i,并将其固定为1.然后,在组件i未采用的[1,3N]值中,选择最小的组件,将唯一能够获取该值的组件j修复为相同的值。以相同的方式继续,固定(N-H-1)组件。之后,您确定了一组最多3M元组。您可以生成完整的元组集(通过对剩余的H + 1组件进行穷举搜索),然后对此集进行排序,获得前M个元组。
答案 2 :(得分:0)
您可以在O(N.M)时间内构建解决方案。这里的解决方案将是M个排列的列表,每个排列都有相关的分数,按分数的降序排列。
如果N为1,则解决方案很简单:您只需根据他们的分数(如果M <3,则减少解决方案)来订购您获得的3个结果。也就是说,如果结果得分是胜利,失败和平局,那么解决方案就是:
(按分数排序,当然是降序)。
现在进行迭代步骤。给出大小为N-1的问题的解决方案(大小为M),然后考虑一个新的比赛结果(赢,输,抽奖)。
只需在每个M解决方案中添加胜利,输掉,绘制分数,然后求助。 也就是说,假设解决方案是[(res_1,score_1),(res_2,score_2),...(res_M,score_M)],那么:
新解决方案将是sorted(wins + loses + draws)[:M]
,这是组合解决方案中的第一个M最大解决方案。注意,您可以使用mergesort中的合并步骤在O(M)时间内执行此排序步骤。
这是一些演示算法的草率代码。对于紧密的解决方案,列表切片和字符串附加需要用O(1)替换,并且使用合并步骤而不是一般排序。
def results(outcomes, M):
if len(outcomes) == 1:
return sorted(zip(outcomes[0], 'WLD'), reverse=True)[:M]
tr = results(outcomes[1:], M)
result = []
for outscore, outcome in zip(outcomes[0], 'WLD'):
result.extend((sc + outscore, pr + outcome) for sc, pr in tr)
return sorted(result, reverse=True)[:M]
# Outcome scores for each contest, in order (win, lose, draw).
testcase = [(1, 7, 4), (2, 8, 5), (3, 9, 6)]
print results(testcase, 5)
输出结果为:
[(24, 'LLL'), (21, 'LLD'), (21, 'LDL'), (21, 'DLL'), (18, 'WLL')]
答案 3 :(得分:0)
GCC 4.7.3:g ++ -Wall -Wextra -std = c ++ 0x perm.cpp
#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
using ints = std::vector<int>;
template <typename T>
bool equal(const std::vector<T>& a, const std::vector<T>& b) {
return std::equal(std::begin(a), std::end(a), std::begin(b)); }
bool is_strictly_monotonic(const ints& p) {
return std::is_sorted(std::begin(p), std::end(p), std::less_equal<int>()); }
ints gen_seq(int size, int init) {
ints v(size);
for (auto& e : v) { e = ++init; }
return v; }
ints begin_combination(const ints& p, int mod) {
return gen_seq(p.size(), -1); }
// Not the same as a normal STL end. This is actually the last combination.
ints end_combination(const ints& p, int mod) {
return gen_seq(p.size(), mod - p.size()); }
// This is the most complicated bit of code, but what it does is
// straightforward. This function treats the input sequence as the
// (radix m+1) digits in a counter. It increments the counter by one, while
// maintaining the constraint that the digits in the sequence be strictly
// monotonic. This means that some numbers in the regular counter sequence
// will be skipped, for example the sequence after {1, 2, 9} (radix 10)
// is {1, 3, 4}. Like all digital counters it wraps on overflow.
void inc_monotonic_seq(ints& p, const int m) {
assert(is_strictly_monotonic(p));
int i = p.size() - 1;
// scan right to left for number to increment
while (i != -1) {
if (p[i] < m) {
++p[i];
ints::size_type j = i + 1;
// propogate carry left to right
while (j != p.size()) {
p[j] = p[j - 1] + 1;
if (m < p[j]) { break; }
++j; }
if (j == p.size()) { break; } }
--i; }
// wrap around
if (i == -1) { p = begin_combination(p, m); }
assert(is_strictly_monotonic(p));
}
// A combination is valid if each contest is represented once.
bool is_valid_combination(const ints& p, const ints& contests) {
auto t(p);
for (auto& e : t) { e = contests[e]; }
std::sort(std::begin(t), std::end(t));
return is_strictly_monotonic(t); }
// This is the second most complicated bit of code. It calculates the
// combination following p in the ordered combination sequence. Combinations
// are ordered lexically by the sort of their elements, for example:
// {6, 1, 2} < {3, 1, 5} because {1, 2, 6} < {1, 3, 5}. Further, it enforces
// the constraint that each digit in the combination must be drawn from the
// contest in that position.
bool next_combination(ints& p, const ints& contests) {
std::sort(std::begin(p), std::end(p));
const auto mx = end_combination(p, contests.size() - 1);
do {
if (equal(p, mx)) { return false; }
inc_monotonic_seq(p, contests.size() - 1);
} while (!is_valid_combination(p, contests));
// Sort in contest order: contest0, contest1, ...
for (int i = 0; i < p.size(); ++i) {
while (i != contests[p[i]]) {
std::swap(p[i], p[contests[p[i]]]); } }
return true;
}
int main() {
const int N = 256; // number of contests
const int M = 500; // number of most preferably ranked outcomes to display
// This is the third most complicated bit of code. The following vector is
// a map from priorities to contests. For example, the following 3 contest
// win-tie-loss priorities {{0, 3, 6}, {1, 4, 7}, {2, 4, 8}} are stored as
// {0, 1, 2, 0, 1, 2, 0, 1, 2}. This inversion is possible because the
// contest outcome priorities are required to be disjoint since there is
// a total ordering over the outcomes. Note, the highest priority is 0
// (not 1 as in the specification).
ints contests(3 * N); // map priorities to contests
int c = 0;
for (auto& e : contests) { e = c % N; ++c; }
// Highest priority combination.
ints p(N);
p = begin_combination(p, contests.size() - 1);
int total = 1;
do {
// Finally, doing some sort of mapping here from priorities to strings,
// as in the problem specification, is trivially done.
std::copy(std::begin(p), std::end(p), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
if (M < ++total) { break; }
} while(next_combination(p, contests));
return 0;
}
几点说明: