2006年10月27日

前几天同学提到编程实现排列组合的算法, 要能把排列组合的结果输出. 想了一段时间未果, 同学的思路是使用递归, 比如求解找出 n 个数中 m 个数的排列和组合:

 step a) 求解后 n-1 个数中 m 个数的排列组合; // P(n-1, m) 或 C(n-1, m)

 step b) 求解后 n-1 个数中 m-1 个数的排列组合; // P(n-1, m-1) 或 C(n-1, m-1)

 基本情况: m 为 0 时返回.

整个过程复杂度 O(n^2), 空间需要 P(n, m)*m 或者 C(n, m)*m.

下面是从网上找到的排列组合算法, 我觉得挺巧的, 最后面是一个利用 C++ STL 库函数实现的排列组合算法 (与前面的无关), 复杂度未知 (应该取决于库函数的内部实现吧).

1.1 组合算法

    本程序的思路是开一个数组,其下标表示 1 到 m 个数,数组元素的值为 1 表示其下标代表的数被选中,为 0 则没选中。

    首先初始化,将数组前 n 个元素置 1,表示第一个组合为前 n 个数。

    然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为“01”组合,同时将其左边的所有“1”全部移动到数组的最左端。

    当第一个“1”移动到数组的 m-n 的位置,即 n 个“1”全部移动到最右端时,就得到了最后一个组合。

    例如求 5 中选 3 的组合:
  1   1   1   0   0   //1,2,3
  1   1   0   1   0   //1,2,4
  1   0   1   1   0   //1,3,4
  0   1   1   1   0   //2,3,4
  1   1   0   0   1   //1,2,5
  1   0   1   0   1   //1,3,5
  0   1   1   0   1   //2,3,5
  1   0   0   1   1   //1,4,5
  0   1   0   1   1   //2,4,5
  0   0   1   1   1   //3,4,5

1.2 全排列算法

    从 1 到 N,输出全排列,共 N! 条。

    分析:用 N 进制的方法吧。设一个 N 个单元的数组,对第一个单元做加一操作,满 N 进一。每加一次一就判断一下各位数组单元有无重复,有则再转回去做加一操作,没有则说明得到了一个排列方案。


2 C++ STL 的排列组合方法实现: 最近一直在看 << C++ 标准程序库 >> 这本书,在看到"变序性算法"部分的时候,发现两个函数 next_permutation, prev_permutation 对于我们平时处理排列组合的问题很有帮助,根据书上的介绍写了两个个测试函数:

void permutate()
{
    vector<
int> v;

    INSERT_ELEMENTS(v, 1,3);

    PRINT_ELEMENTS(v, "myself: ");
 
    
while(next_permutation(v.begin(), v.end()))
    {
        PRINT_ELEMENTS(v, "");
    }
}

void conbine()
{
    vector<
int> v;

    INSERT_ELEMENTS(v, 1,3);

    PRINT_ELEMENTS(v, "myself: ");
 
    sort(v.begin(), v.end(), greater<
int>());//增加排序(降序)
 
    
while (prev_permutation(v.begin(), v.end()))
    {
        PRINT_ELEMENTS( v, ""
);
    }
}
posted @ 2006-10-27 18:04 ScorpioLove 阅读(739) 评论(1) 编辑
 
摘要: 前几天无聊, 舍友拿来一玩具"九连环". 把玩之中, 觉得过程类似"汉诺塔", 遂考虑写个程序模拟一下.

其实过程挺简单的, 关键在于, "九连环"中除了第一个环, 其它所有的环都套着它前面的环, 因此, 要操作第 n 个环 (安装或者拆卸), 则第 n-1 个环必须在上面, 而第 n-2 个之前的所有环都不能在上面.

两个关键函数:阅读全文
posted @ 2006-10-27 17:02 ScorpioLove 阅读(205) 评论(0) 编辑
 
前一段时间看到一个往年程序竞赛的题解, 有一个题目说的是求 100 的阶乘末尾有多少个 0. 题解中给出的讲解提到, 一个数 n 的阶乘末尾有多少个 0 取决于从 1 到 n 的各个数的因子中 2 和 5 的个数, 而 2 的个数是远远多余 5 的个数的, 因此求出 5 的个数即可. 题解中给出的求解因子 5 的个数的方法是用 n 不断除以 5, 直到结果为 0, 然后把中间得到的结果累加. 例如, 100/5 = 20, 20/5 = 4, 4/5 = 0, 则 1 到 100 中因子 5 的个数为 (20 + 4 + 0) = 24 个, 即 100 的阶乘末尾有 24 个 0. 其实不断除以 5, 是因为每间隔 5 个数有一个数可以被 5 整除, 然后在这些可被 5 整除的数中, 每间隔 5 个数又有一个可以被 25 整除, 故要再除一次, ... 直到结果为 0, 表示没有能继续被 5 整除的数了.

今天无意间看到有人问 1000 的阶乘有几位数, 上来就用上面的方法算了一下, 249, 又读一遍题目, 才发现是求所有的位数, 想了好久也没有思路, 无奈用 Python 算了一把, 结果有 2568 位, 可是依然不知道如何计算 1000 阶乘的位数, 还好通过结果验证了末尾有 249 个 0 是正确的...

--------

后来浏览小百合时找到了一个方法,参见: 后续, 求解


posted @ 2006-10-27 16:35 ScorpioLove 阅读(4926) 评论(3) 编辑