程序员的组合学?

时间:2012-01-10 04:21:57

标签: combinatorics

我开始编写C#Silverlight程序,试图找到解决旅行销售人员问题的强力解决方案。但是我一直试图找出所有可能的路线。

对于我的程序,我生成随机点并尝试找到可以加入它们的最短行,而不会访问任何两次。

所以如果我有三个点A,B和& C我想找到A,B和A的所有不同组合。 C,其中每个只使用一次,并且该集合与反转时已经找到的另一个集合不同。

例如: ABC ACB BAC

但是如何计算任意数量的点的所有组合?

我正在编写这个程序以获得乐趣,我现在更感兴趣的是找到一个很好的资源来学习如何解决编程中的组合问题。我发现的用于学习组合学的一切都告诉我如何找到可能的组合数量,并且实际上枚举所有可能的组合是没用的。

3 个答案:

答案 0 :(得分:1)

如果您正在加入此类事情,我建议您尝试项目euler上的一些问题,例如: http://projecteuler.net/problem=15

在pythons itertools模块中,它有一些示例代码示例。 您可以将示例代码转换为您选择的编程语言。

http://docs.python.org/library/itertools.html

示例函数:

product('ABCD', repeat=2)       AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
permutations('ABCD', 2)     AB AC AD BA BC BD CA CB CD DA DB DC
combinations('ABCD', 2)     AB AC AD BC BD CD
combinations_with_replacement('ABCD', 2)        AA AB AC AD BB BC BD CC CD DD

示例代码:

def combinations(iterable, r):
    # combinations('ABCD', 2) --> AB AC AD BC BD CD
    # combinations(range(4), 3) --> 012 013 023 123
    pool = tuple(iterable)
    n = len(pool)
    if r > n:
        return
    indices = range(r)
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield tuple(pool[i] for i in indices)

请注意,在上面的问题中,如果你允许一个从直线距离的点x1,y1到点x2,y2,那么它不是同一个问题。 (因为您可以对点进行排序并将它们放入空间数据结构中)。我认为在旅行商问题上,你应该有“多风/丘陵道路”,这样即使两个点在x和y方面都很接近,它们也可能有一个很大的加权边连接它们。

答案 1 :(得分:0)

这是我的C#类来查找排列或组合:

public static class IEnumerableExtensions
{
    public static IEnumerable<IEnumerable<T>> Arrange<T>(this IEnumerable<T> elements,
        int places, bool allowRepeats = true, bool orderMatters = true)
    {
        return orderMatters ?
            Permutate(elements, places, allowRepeats) :
            Combine(elements, places, allowRepeats);
    }

    public static IEnumerable<IEnumerable<T>> Permutate<T>(this IEnumerable<T> elements, int places, bool allowRepeats = false)
    {
        foreach (var cur in elements)
        {
            if (places == 1) yield return cur.Yield();
            else
            {
                var sub = allowRepeats ? elements : elements.Where(v => !v.Equals(cur));
                foreach (var res in sub.Permutate(places - 1, allowRepeats))
                {
                    yield return res.Prepend(cur);
                }
            }
        }
    }

    public static IEnumerable<IEnumerable<T>> Combine<T>(this IEnumerable<T> elements, int places, bool allowRepeats = false)
    {
        int i = 0;
        foreach (var cur in elements)
        {
            if (places == 1) yield return cur.Yield();
            else
            {
                var sub = allowRepeats ? elements.Skip(i++) : elements.Skip(i++ + 1);
                foreach (var res in sub.Combine(places - 1, allowRepeats))
                {
                    yield return res.Prepend(cur);
                }
            }
        }
    }

    public static IEnumerable<T> Yield<T>(this T item)
    {
        yield return item;
    }

    static IEnumerable<T> Prepend<T>(this IEnumerable<T> rest, T first)
    {
        yield return first;
        foreach (var item in rest)
            yield return item;
    }
}

用法:

        var places = new char[] { 'A', 'B', 'C' };
        var routes = places.Permutate(3).ToArray();

        //to remove reverse routes:
        var noRev = (from r1 in routes
                     from r2 in routes
                     where r1.SequenceEqual(r2.Reverse())
                     select (r1.First() < r2.First() ? r1 : r2)).Distinct();

答案 2 :(得分:0)

这是 Python 中的解决方案。第一个函数是一个递归函数,它生成与输入列表长度相同 n 的所有排列 P(n,n)。第二个函数运行第一个函数并过滤掉任何已经存在逆序的排列。

def all_perms(elements):
    """
    Recursive function to generate all permutations
    :param elements: a list
    """
    if len(elements) <=1:
        yield elements
    else:
        for perm in all_perms(elements[1:]):
            for i in range(len(elements)):
                yield perm[:i] + elements[0:1] + perm[i:]

def filtered_perms(elements):
    """
    Filters out any permutation whose reverse already exists
    :param elements: a list
    """
    result = []
    for perm in all_perms(elements):
        if list(reversed(perm)) not in result:
            result.append(perm)
    print(result)

filtered_perms(["A", "B", "C"])
#[['A', 'B', 'C'], ['B', 'A', 'C'], ['B', 'C', 'A']]