【问题标题】:Getting all combinations of splitting an array into two equally sized groups in Julia在 Julia 中获取将数组拆分为两个大小相等的组的所有组合
【发布时间】:2013-10-04 01:38:19
【问题描述】:

给定一个包含 20 个数字的数组,我想提取两组的所有可能组合,每组有 10 个数字,顺序并不重要。

combinations([1, 2, 3], 2)

在 Julia 中会给我从数组中抽取的两个数字的所有可能组合,但我还需要未抽取的那些...

【问题讨论】:

  • 也许组合应该返回两组值。你可能想打开一个关于这个 API 问题的问题。
  • 默认这样做会破坏combinations(1:1000, 2)的性能,这一定是常见的情况,目前效率很高。

标签: combinations julia


【解决方案1】:

您可以使用setdiff 来确定任何向量中缺少的项目,例如,

y = setdiff(1:5, [2,4])

产生[1,3,5]

【讨论】:

  • 这种方法的问题是可能存在重复值,即。如果我们有 [1,2,3,4,4,4,5,6,7] 并想将其分成两组, setdiff() 不会给我们“剩余值”,它会删除所有重复项。
  • 那么不要有重复的值。只需执行combinations([1:9],4) 并通过val[index] 获取您的实际值,其中index 来自combinations(或setdiffcombinations 的结果上)和val=[1,2,3,4,4,4,5,6,7]。这应该比基于removeall!的版本效率更高。
  • 事实上,甚至比setdiff 更好的可能是flag=falses(9); flag[index]=true; {value[flag], value[!flag]}
  • 通过 val[index] 获取实际值非常棒——一种扭转局面的好方法——而且应该比我上面的版本更有效。谢谢!
【解决方案2】:

玩了一会儿之后,我想出了这段代码,它似乎可以工作。我相信它可以写得更优雅,等等。

function removeall!(remove::Array, a::Array)
    for i in remove
        if in(i, a)
            splice!(a, indexin([i], a)[1])
        end
    end
end

function combinationgroups(a::Array, count::Integer)
    result = {}
    for i in combinations(a, count)
        all = copy(a)
        removeall!(i, all)
        push!(result, { i; all } )
    end
    result
end

combinationgroups([1,2,3,4],2)

6-element Array{Any,1}:
 {[1,2],[3,4]}
 {[1,3],[2,4]}
 {[1,4],[2,3]}
 {[2,3],[1,4]}
 {[2,4],[1,3]}
 {[3,4],[1,2]}

【讨论】:

    【解决方案3】:

    基于@tholy 关于不使用实际数字的评论,我可以使用位置(以避免数字不唯一的问题)和 setdiff 来获取“其他组”(未选择的数字),我想出了与以下。第一个函数根据索引从数组中获取值(即 arraybyindex([11,12,13,14,15], [2,4]) => [12,14])。这似乎是标准库的一部分(我确实寻找过,但可能错过了)。

    第二个函数执行上面组合组所做的工作,创建一定大小的所有组及其互补组。它可以自己调用,也可以通过第三个函数调用,该函数提取所有可能大小的组。有可能这一切都可以写得更快,更惯用。

    function arraybyindex(a::Array, indx::Array)
        res = {}
        for e in indx
            push!(res, a[e])
        end
    
        res
    end
    function combinationsbypos(a::Array, n::Integer)
        res = {}
        positions = 1:length(a)
        for e in combinations(positions, n)          
            push!(res, { arraybyindex(a, e) ; arraybyindex(a, setdiff(positions, e)) })
        end
        res
    end
    
    function allcombinationgroups(a::Array)
        maxsplit = floor(length(a) / 2)
        res = {}
        for e in 1:5
            println("Calculating for $e, so far $(length(res)) groups calculated")
            push!(res, combinationsbypos(a, e))
        end
        res
    end
    

    在 3 岁的 MacBook pro 上在 IJulia 中运行它会得到

    @time c=allcombinationgroups([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])
    println(length(c))
    c
    
    Calculating for 1, so far 0 groups calculated
    Calculating for 2, so far 20 groups calculated
    Calculating for 3, so far 210 groups calculated
    Calculating for 4, so far 1350 groups calculated
    Calculating for 5, so far 6195 groups calculated
    Calculating for 6, so far 21699 groups calculated
    Calculating for 7, so far 60459 groups calculated
    Calculating for 8, so far 137979 groups calculated
    Calculating for 9, so far 263949 groups calculated
    Calculating for 10, so far 431909 groups calculated
    elapsed time: 11.565218719 seconds (1894698956 bytes allocated)
    Out[49]:
    616665
    
    616665-element Array{Any,1}:
     {{1},{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}}
     {{2},{1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}}
      ⋮                                                       
     {{10,12,13,14,15,16,17,18,19,20},{1,2,3,4,5,6,7,8,9,11}}
     {{11,12,13,14,15,16,17,18,19,20},{1,2,3,4,5,6,7,8,9,10}}
    

    即。每秒计算 53,334 个组。

    作为对比,使用相同的外部 allcombinationgroups 函数,但是用对 combinationgroups 的调用替换对 combinationbypos 的调用(参见上一个答案),速度要慢 10 倍。

    然后,我按照@tholy 的建议,使用 true 或 false 标志按索引组重写了数组(我不知道如何使用 [] 使其工作,所以我明确地使用了 setindex!,并将其移动到一个函数中. 再加速 10 倍!1 秒内 616,665 组!

    最终代码(到目前为止):

    function combinationsbypos(a::Array, n::Integer)
        res = {}
        positions = 1:length(a)
        emptyflags = falses(length(a))
        for e in combinations(positions, n)       
            flag = copy(emptyflags)
            setindex!(flag, true, e)
            push!(res, {a[flag] ; a[!flag]} )
        end
        res
    end
    
    function allcombinationgroups(a::Array)
        maxsplit = floor(length(a) / 2)
        res = {}
        for e in 1:maxsplit
            res = vcat(res, combinationsbypos(a, e))
        end
        res
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      • 2017-03-09
      相关资源
      最近更新 更多