根据tcl中的总和对列表中的元素进行分组

时间:2018-10-04 17:03:46

标签: tcl

我有一个列表

set num "20 10 40 50 25 15"

我希望对输出进行分组,以使每组的总和不超过60。 在这种情况下,输出:

{20 40} {10 50} {25 15}

我写了以下代码段

set num "20 10 40 50 25 15"
for {set i 0} {$i < 4} {incr i} {
    for {set j 0} {$j < 4} {incr j} {
        if {$i == $j} {continue}
        if {[expr  [lindex $num $i] + [lindex $num $j] ] == 60 } {
        puts "[lindex $num $i]  [lindex $num $j]"}
    }
}

输出:

20  40
10  50
40  20
50  10

我正在尝试删除重复项,并尝试获得总和小于60的组合

2 个答案:

答案 0 :(得分:1)

您需要做的是编写一个在列表中找到最大对的过程,第二个过程从列表中删除一对数字(同时注意重复),然后将它们组合在一起以进行整体任务。

以这种方式拆分任务是程序员学习要做的最重要的事情之一。正确处理需要实践和经验。

proc findPair {list limit} {
    # Variables to hold our best matches so far
    set maxval -inf;  # Negative infinity is less than every other number
    set maxpair {}

    for {set idx1 0} {$idx1 < [llength $list]} {incr idx1} {
        set v1 [lindex $list $idx1]

        # Optimization: make idx2 always greater than idx1
        for {set idx2 [expr {$idx1 + 1}]} {$idx2 < [llength $list]} {incr idx2} {
            set v2 [lindex $list $idx2]
            set sum [expr {$v1 + $v2}]

            if {($sum <= $limit) && ($sum > $maxval)} {
                # Save what we've found as our new best choice
                set maxval $sum
                set maxpair [list $v1 $v2]
            }
        }
    }
    # This variable now has the first, best option...
    # ... or the empty list if we can't find anything that satisfies.
    return $maxpair
}

您可能想考虑为什么我要确保$idx2总是大于$idx1(如果相反,会发生什么情况;为什么我不关心这种情况? )。

proc removePair {listvar pair} {
    # Make variable in caller also be a variable here; THIS IS CLEVER MAGIC
    upvar 1 $listvar list

    foreach value $pair {
        # Find where the value is
        set idx [lsearch -exact $list $value]
        # Remove the element at the $idx'th position
        set list [lreplace $list $idx $idx]
    }
}

现在有了这些,我们可以解决整个问题:

set numbers {20 10 40 50 25 15}
set limit 60

while {[llength $numbers] > 0} {
    set pair [findPair $numbers $limit]
    if {[llength $pair] > 0} {
        # We've found another pair. Great! Print it out
        puts "found pair: $pair"
        # NO ‘$’ in front of ‘numbers’; we are passing the VARIABLE NAME not the contents
        removePair numbers $pair
    } else {
        # No possible pairs left! This is a failure case
        puts "remaining unpairable numbers: $numbers"
        # Stop the search
        break
    }
}

输出是:

found pair: 20 40
found pair: 10 50
found pair: 25 15

我觉得还可以。

答案 1 :(得分:0)

我有一种印象,那就是这是经典的课程作业,问题是缺少与讨论适当解决方案相关的细节(例如,空间与时间复杂性的任何限制,重复的元素等)。

尽管多纳尔的回答本身是完整的,但这只是很多人可能提出的一个答案。这里有一个更常规的解决方案架构(假设正在寻找就地解决方案),包括:

  1. 从排序列表开始(将总体复杂度设置为Tcl的[lsort])。
  2. 从两个方向(即从头和尾开始)遍历此排序列表,直到它们重叠。
  3. 对于当前头和尾的每个候选对,确定是否满足约束条件(例如,对和限制)。根据决定,继续前进头或尾索引(使用incr)。

可能的骨架看起来像这样:

proc pairwiseByCappedPairSum {list limit} {

    set list [lsort -integer $list]; # use -unique flag in case of duplicates
    set fromStart 0
    set fromEnd 0

    while {$fromStart < ([llength $list]-$fromEnd-1)} {
        set v1 [lindex $list $fromStart]
        set v2 [lindex $list end-$fromEnd]

        ## ENTER YOUR FRAGMENT HERE:
        ## -------------------------
        # if {...} {
        #   incr fromEnd
        # } else {
        #    puts [list $v1 ...]
        #    incr fromStart
        # }
    }
}

完成后,以这种方式调用:

set num "20 10 40 50 25 15"
set limit 60
pairwiseByCappedPairSum $num $limit

它应该打印出来:

10 {15 20 25 40 50}
15 {20 25 40}
20 {25 40}

我觉得还可以。