快速计算从n中任选m的所有组合

Posted: 2016-09-23 in 设计
来源: http://lifesinger.org/blog/2009/09/javascript-quick-combine/

Posted on September 25th, 2009 in 前端开发 by lifesinger

/**
 * 快速组合算法
 * 从 n 中任选 m(0 < m <= n) 个数的所有组合
 */
function quick_combine(n, m) {
    var t = ((1 << n) - (1 << n - m)).toString(2),
        r = [], s, p1, p2;
    while((r.push(t), p1 = t.indexOf("10")) >= 0) {
        s  = t.slice(0, p1);
        p2 = s.indexOf("1");
        t = (p2 > 0 ? ((1 << p1) - (1 << p2)).toString(2) : s)
             "01" + t.slice(p1 + 2);
    }
    return r;
}

算法思路:

假设场景为从 [a, b, c, d, e] 里, 5 选 3.

先设定第一个和最后一个组合为:
s = 1 1 1 0 0 // a,b,c
e = 0 0 1 1 1 // c,d,e

Step1. 从左到右找出第 i 个组合中的 10, 转换为 01, 并将 01 左边的 1 全部移动到最左边,得到新组合 t
Step2. 如果 t 不为 e, 继续 Step1

按照以上思路,可以得到:

1 1 1 0 0
1 1 0 1 0
1 0 1 1 0
0 1 1 1 0
1 1 0 0 1
1 0 1 0 1
0 1 1 0 1
1 0 0 1 1
0 1 0 1 1
0 0 1 1 1

共 10 种组合

网上流传的递归方案(好像是月影写的?没找到源头):

/**
 * 递归组合算法
 * 从 arr[1...n] 中任选 num(0 < num <= n) 个数的所有组合
 */
function combine(arr, num) {
    var r = [];
    (function f(t, a, n) {
        if (n == 0return r.push(t);
        for (var i = 0, l = a.length; i <= l - n; i++) {
            f(t.concat(a[i]), a.slice(i + 1), n - 1);
        }
    })([], arr, num);
    return r;
}

两种方案的性能对比: combine-test.html

在 Firefox, Chrome, Safari 浏览器中,快速组合算法优势非常明显。
在 IE 浏览器中,计算量比较小时,快速组合算法依旧有优势;但计算量大时,无优势,甚至不如递归。

2009-09-27: 修改快速组合算法,用 Math 代替 regex replace, 性能立刻提升,在所有浏览器下保持优势。

51js 讨论贴:http://bbs.51js.com/viewthread.php?tid=85574

Leave a Reply

Your email address will not be published. Required fields are marked *