可被n整除的和

时间:2016-02-20 23:13:25

标签: algorithm dynamic-programming modulo

这是算法课程简介中的一个问题:

  

你有一个 n 随机正整数的数组(数组没有   需要排序或元素独特)。建议 O(n)算法   找到最大的元素总和,可以被 n 整除。

使用动态编程在 O(n 2 中找到它并使用余数0,1,2,...,n存储最大总和相对容易1.这是一个JavaScript代码:

function sum_mod_n(a)
{
    var n = a.length;

    var b = new Array(n);
    b.fill(-1);

    for (var i = 0; i < n; i++)
    {
        var u = a[i] % n;
        var c = b.slice();

        for (var j = 0; j < n; j++) if (b[j] > -1)
        {
            var v = (u + j) % n;
            if (b[j] + a[i] > b[v]) c[v] = b[j] + a[i];
        }

        if (c[u] == -1) c[u] = a[i];
        b = c;
    }

    return b[0];
}

对于连续元素,在 O(n)中找到它也很容易,存储部分和MOD n。另一个样本:

function cont_mod_n(a)
{
    var n = a.length;

    var b = new Array(n);
    b.fill(-1);

    b[0] = 0;

    var m = 0, s = 0;

    for (var i = 0; i < n; i++)
    {
        s += a[i];
        var u = s % n;
        if (b[u] == -1) b[u] = s;
        else if (s - b[u] > m) m = s - b[u];
    }

    return m;
}

但是一般情况下 O(n)怎么样?任何建议将不胜感激!我认为这有一些东西要处理线性代数,但我不确定究竟是什么。

编辑:这实际上可以在 O(n log n)中完成吗?

2 个答案:

答案 0 :(得分:1)

既然你没有指定 random 意味着什么(统一?如果是这样,在什么时间间隔?),唯一的一般解决方案是任意阵列的解决方案,我不认为你可以得到比O更好的(n 2 )。这是Python中的动态编程算法:

def sum_div(positive_integers):
    n = len(positive_integers)
    # initialise the dynamic programming state
    # the index runs on all possible reminders mod n
    # the DP values keep track of the maximum sum you can have for that reminder
    DP = [0] * n
    for positive_integer in positive_integers:
        for remainder, max_sum in list(enumerate(DP)):
            max_sum_next = max_sum + positive_integer
            remainder_next = max_sum_next % n
            if max_sum_next > DP[remainder_next]:
                DP[remainder_next] = max_sum_next
    return DP[0]

如果您对数组中的值有一个上限,则可能可以找到更快的解决方案,例如: 名词

答案 1 :(得分:0)

非常有趣的问题! 这是我的JS代码。我不认为O(n ^ 2)可以降低,因此我认为方法是找到一种在基准测试方面更有效的算法。

我的(校正的)方法归结为探索和的路径,直到计算下一个匹配的路径(即可被_n整除)。当找到下一个总和时,源数组会逐渐缩小。

(我在顶部提供了不同的例子)

var _a = [1000, 1000, 1000, 1000, 1000, 1000, 99, 10, 9] ;
//var _a = [1000, 1000, 1000, 1000, 1000, 1000, 99, 10, 9, 11] ;
//var _a = [1, 6, 6, 6, 6, 6, 49] ;
//var _a = [ -1, 1, 2, 4 ] ;
//var _a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ;
//var _a = [1,1,1,1,1,1] ;
var _n = _a.length, _del_indexes = [] ;
var _rec = 0, _sum = 0, _start = 0, _test = 0 ;

console.log( "input array : ", _a );
console.log( "cardinality : ", _a.length );

while( _start < _a.length )
{
    _test = 0 ;
    for( var _i = _start ; _i < _a.length ; _i++ )
    {
             _sum += _a[_i%_n] ;
             _del_indexes.push( _a[_i%_n] );
             if ( ( _sum % _n ) == 0 )
             {
                 _rec = _sum ;
                 _test = 1 ;
                 break ;
             }
    }

    if ( _test )
    {
        for( var _d = 0 ; _d < _del_indexes.length ; _d++ ) _a.splice( _a.indexOf( _del_indexes[_d] ), 1 ) ;
        _start = 0 ;
    }
    else _start++ ;

    _del_indexes = [] ;
    _sum = _rec ;
}

console.log( "Largest sum % " + _n + " is : ", _rec == 0 ? "none" : _rec );