递归计算int列表列表的可能总和

时间:2013-01-29 16:11:17

标签: sml smlnj ml

我正在努力编写代码,这将计算int列表列表的总和。例如,如果我们考虑以下列表

[[1],[1,5],[7],[2,3,4]]

我们可以获得不同的可能总和,具体取决于我们每次选择的整数:

[11,12,13,15,16,17]

有人可以提供可能的方法(不需要代码)我能解决吗?

3 个答案:

答案 0 :(得分:1)

以下是试图打破可读和希望可以理解的部分的步骤

fun sums xss =
    let
      fun extend xss y = map (fn xs => y :: xs) xss
      fun extend' xss ys = foldl (fn (y, b) => extend xss y @ b) [] ys
      fun extend'' xss = foldl (fn (xs,b) => extend' b xs) [[]] xss
      fun sum xs = foldl op+ 0 xs
    in
      map sum (extend'' xss)
    end


- sums [[1],[1,5],[7],[2,3,4]];
val it = [17,13,16,12,15,11] : int list

显然,大多数函数都可以按正确的顺序将参数作为对进行,因此直接给出了map和fold函数,而不是将它们包装在匿名函数中。

答案 1 :(得分:1)

这是我尝试使用列表和模式匹配来制作尾递归(有点)版本的算法。它是用OCaml编写的,而不是SML,需要内部列表的升序

let reverse list = 
    let rec iter list acc :int list = match list with
    [] -> acc
    | x::xs -> iter xs (x::acc)
 in iter list [];;

let listAdd num list = 
  let rec iter num list acc :int list = match list with
     [] -> acc
    | x::xs -> iter num xs ((x + num)::acc)
  in reverse( iter num list []);;   

let listMerge listX listY = 
  let rec iter listX listY acc : int list = match listX with 
     [] -> (reverse acc) @ listY
     | x :: xs -> match listY with 
        [] -> (reverse acc ) @ listX
        | y :: ys -> match ( compare x y ) with
           0  ->  iter xs ys ( x::acc )
          |(-1) ->  iter xs listY ( x::acc )    
          | 1 ->  iter listX ys ( y::acc )
  in iter listX listY [];;  

let listSums listX listY =
 let rec iter listX listY acc = match listX with 
   [] -> acc
   | x :: xs -> iter xs listY ( listMerge acc (listAdd x listY ) )
 in iter listX listY [];;  

let possibleSums shallowList = match shallowList with 
   [] -> []
   | headList :: lists -> 
      let rec iter acc lists  = match lists with 
          [] -> acc
          | headList :: other -> iter ( listSums acc headList ) other  
      in iter headList lists;;

此处possibleSums [[1];[1;5];[7];[2;3;4]];;评估为int list = [11; 12; 13; 15; 16; 17]

所以基本上合乎逻辑的步骤是:

  • reverse函数只是在生成累加器后恢复顺序的实用程序 - 尾递归优化的常用概念
  • listAdd函数,以避免在构建摘要时使用map实用程序
  • listMerge函数等于set union以将排序列表用作整数集
  • 用于将总和映射到两组笛卡尔积的listSums函数
  • posibleSums函数,用于将所有集合的完整笛卡尔积的映射求和的最终级别。

该算法不仅通过TCO避免了递归中的堆栈溢出,而且还为此计算问题提供了近似最优解。

此解决方案还可以通过添加过早merge sorting并从集合中删除重复元素来进一步改进,因此内部集合顺序现在无关紧要:

let split lst = 
    let rec iter lst partX partY = match lst with
        [] -> partX,partY
        | x::xs -> iter xs partY (x::partX)
    in iter lst [] [];;

let rec mergeSort lst = match lst with 
    [] -> []
    |[x] -> [x]
    | _ -> let partX,partY = split lst in
           listMerge (mergeSort partX) (mergeSort partY);;

let possibleSums shallowList = match shallowList with 
   [] -> []
   | headList :: lists -> 
      let rec iter acc lists  = match lists with 
          [] -> acc
          | headList :: other -> iter ( listSums acc (mergeSort headList) ) other  
      in iter (mergeSort headList) lists;;

您可以通过简单示例测试其效率。让我们定义重复函数来制作重复列表:

 let rec repeat elem n = match n with
     0 -> []
    | _ -> elem :: (repeat elem (n - 1));;

在这种情况下,possibleSums( repeat( repeat 1 30) 30 )会立即计算出正确的单身答案int list = [30]。虽然更直接的解决方案(如Jesper的解决方案)将永远这样做。

答案 2 :(得分:-2)

我会尝试使用递归循环来解决它 - 伪代码:

sum = 0;

while (i < arrayOfArrays.length ) {
     sumUp(arrayOfArrayElements);
     i ++
 }

 function sumUp() {
     while( j < arrayOfArrayElement.length){
     sum = sum + arrayOfArrayElement[j].val
     j ++

  }


}

所以你调用一个函数来汇总一个数组的每个数组元素,并在一个函数内汇总这些值,该函数为该数组数组中的每个元素调用该函数。 LOLZ

  
      
  1. 创建一个带有一组int的函数,并使用for或while循环添加包含的int。
  2.   
  3. 遍历主数组并调用每个包含数组元素的函数进行求和,直到没有剩余元素为止,您可以通过使用数组的.length来实现这一点,该数组应该可以在每个脚本/编程中访问语言
  4.