贝尔数算法

时间:2011-11-07 08:36:07

标签: algorithm

我正在尝试编写一种算法来查找n个数字的排序方式。例如,两个数字表示a和b可以用3种方式排序..

同样,3种数字可以13种方式排列。

我发现我可以使用动态编程解决问题。这就是我想要的是具有代表不同排序的图层。防爆。 a > b有两层,a = b有一层,依此类推。这样我就可以像动态编程一样将它用于以后的目的。但是我不能为它写一个递归关系。有人可以建议我怎么写那个?

6 个答案:

答案 0 :(得分:1)

假设f(n,k)=具有k不等式(以及n-k-1相等)的可能方式的数量,因此: 假设您有n-1个数字,现在您想要添加另一个数字并计算f(n,k),那么我们有两种可能性:

1)那些(n-1)个数中存在(k-1)个不等式,并且有(k + 1)(f(n-1,k-1)个)方法来添加第n个数,所以新的不平等增加了。

2)在那些(n-1)个数中存在k个不等式,并且存在(k + 1)(f(n-1,k))个方法来添加第n个数而没有额外的不等式。

f(n,k) = (k+1)(f(n-1,k-1) + f(n-1,k))

你想要的是它们的总和(从0到n-1),Bellow代码在c#中(仅针对简单情况进行测试),实际上我们只计算了不能生成所有方式的可能方式的数量..

class EqualInequalPermutation
{
    public int OrderingsNumber(int n)
    {
        int[][] f = new int[n+1][];
        for (int i = 0; i < n+1; i++)
        {
            f[i] = new int[n];
            for (int j = 0; j < n; j++)
                f[i][j] = 0;
        }
        f[1][0] = 1;
        int factorial = 1;
        for (int i = 1; i <= n; i++)
        {
            f[i][0] = 1;
            factorial *= i;
            f[i][i - 1] = factorial;
            for (int k = 1; k < n; k++)
            {
                f[i][k] = (k + 1) * (f[i - 1][k - 1] + f[i - 1][k]);
            }
        }
        int answer = 0;
        for (int i = 0; i < n; i++)
        {
            answer += f[n][i];
        }
        return answer;
    }
}

答案 1 :(得分:1)

我发现On-Line Encyclopedia of Integer Sequences对于像这样的问题来说是一个很好的资源。您已经提供了足够的信息来获得答案。显然,对于零数的退化情况,只能进行一次排序。对于单个数字,也只存在一个订单。对于两个,你说有三个排序,三个整数有十三个。搜索1,1,3,13,弹出的第一场比赛是this one,“竞争者在比赛中排名的方式数量,允许联系的可能性。”从那里你会看到这个序列中的前20个左右的结果,以及人们对序列贡献的内容。其中列出的是Mathematica中的递归解决方案(在此重新格式化和扩展以便澄清):

f[0] = 1
f[1] = 1
f[n_] := f[n] = Sum[ Binomial[n,k] * f[n-k], {k,1,n}]   (* memoizes the values *)

如果您愿意,可以使用其他语言轻松实现。

希望这会有所帮助,并且您发现OEIS将来会有用!

答案 2 :(得分:1)

//This is the C++ version of the Same Code  
#include<bits/stdc++.h>

using namespace std;

int main() {
    int n;
    cin>>n;
    vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));
    dp[1][0] = 1;
    int f = 1;
    for(int i = 1; i <= n; ++i) {
        dp[i][0] = 1;
        f = f * i;
        dp[i][i - 1] = f;
        for(int j = 1; j <= n; ++j)
            dp[i][j] = (j + 1) * (dp[i - 1][j - 1] + dp[i - 1][j]);
    }
    int ans = 0;
    for(int i = 0; i < n; ++i)
        ans += dp[n][i];
    cout<<ans;
    return 0;
}

您可以继续求模,因为答案将成倍增加。

答案 3 :(得分:0)

老实说,我认为通过动态编程解决它就像用机枪杀死蚂蚁一样。

你绝对应该使用组合学,因为它不应该那么困难。

如果不存在等式n!(置换),那么你必须计算组合(所有相等的n元组),所以它为3

  

3! + 2 *(3比2)+(3比3)= 13

答案 4 :(得分:0)

以下C#程序输出订单数量和订单本身:

static void Main(string[] args)
{
    if (args.Length < 1)
    {
        Console.WriteLine("Missing argument - the number of items");
        return;
    }
    int n;
    if (!int.TryParse(args[0], out n))
    {
        Console.WriteLine("Could not parse number of items");
        return;
    }
    if (n < 0)
    {
        Console.WriteLine("Number of items must not be negative");
        return;
    }
    var count = GetOrderings(n);
    Console.WriteLine("Total: {0}", count);
}

private static int GetOrderings(int n)
{
    var items = Enumerable.Range(0, n).Select(i => (char)('a' + i)).ToList();
    // Produce distinct orderings of the input items
    return GetOrderings(new List<char>(), items);
}

private static int GetOrderings(List<char> current, List<char> items)
{
    // If we have a complete permutation in current, produce the possible arrangements of signs between them
    if (items.Count == 0) return GetSigns(new List<char>(), current, 0);
    var result = 0;
    for (var i = 0; i < items.Count; i++)
    {
        // nextCurrent = current + i'th element of items
        var nextCurrent = new List<char>(current) { items[i] };
        // nextItems = items excluding the i'th element
        var nextItems = new List<char>(items.Where((c, n) => n != i));
        result += GetOrderings(nextCurrent, nextItems);
    }
    return result;
}

private static int GetSigns(List<char> signs, List<char> items, int n)
{
    if (signs.Count >= items.Count - 1)
    {
        // Once we have sufficient signs, write out the items interleaved with them
        var str = string.Empty;
        for (var i = 0; i < items.Count; i++)
        {
            if (i > 0) str += signs[i - 1];
            str += items[i];
        }
        Console.WriteLine(str);
        return 1;
    }
    var result = GetSigns(new List<char>(signs) { '<' }, items, n + 1);
    // To prevent duplicate orderings, only allow "=" between two items if they are in lexicographic order
    // (i.e. allow "a = b" but not "b = a")
    if (items[n] >= items[n + 1]) return result;
    return result + GetSigns(new List<char>(signs) { '=' }, items, n + 1);
}

示例输出(对于n = 3):

a<b<c
a<b=c
a=b<c
a=b=c
a<c<b
a=c<b
b<a<c
b<a=c
b<c<a
b=c<a
c<a<b
c<a=b
c<b<a
Total: 13

答案 5 :(得分:0)

由于我随机地忽略了这个问题,但是在搜索结果中找不到简单的python实现。为了将来参考,下面提供了python版本的简单实现。

尽管我们可以获得更全面的数学证明here。下面的代码将仅遵循@Saeed Amiri提供的公式:

f(n,k) = (k+1)(f(n-1,k-1) + f(n-1,k))

def bell_number(num: int) -> int:
    dp = [[0] * num for _ in range(num)]
    dp[0][0] = 1
    for n in range(1, num):
        for k in range(num):
            dp[n][k] = (k+1) * (dp[n-1][k-1] + dp[n-1][k])
    return sum(dp[-1])