【问题标题】:Summation in functional programming函数式编程中的求和
【发布时间】:2011-12-21 13:27:27
【问题描述】:

我在网上搜索排除-包含原则,我发现是这样的:


(来自 MathWorld - Wolfram 网络资源:wolfram.com

http://mathworld.wolfram.com/Inclusion-ExclusionPrinciple.html

看不懂公式也没关系,其实我需要的就是实现这个:

例如输入为:

(summation (list 1 2) 3) 其中 (list 1 2) 是 i 和 j,3 是和 n 的极限。

(n 必须是 sigma 但是...)

那么,公式在Scheme中的输出将是:

(列表(列表 1 2)(列表 1 3)(列表 2 3))

如何在 Scheme 或 Haskell 中实现这一点? (对不起我的英语)。

【问题讨论】:

  • 第二个公式末尾悬空的+ 符号是什么?它属于那里吗?
  • 只是从大公式中删除的剩余部分。
  • 我有点困惑。显然您希望函数的结果是一个列表,但您给出的公式计算的是一个数字,而不是一个列表(或一组)。
  • @sepp2k 是的,这些是一些集合的数字。如果解决方案是 (... (list 1 2) ..) 那么,我假设 1 代表 A,2 代表 B。谢谢提问。
  • Daniel Fischer 的answer 应该是公认的答案。如问题中所要求的,其他两个投票最多的答案不计算重叠集并集的长度,这是包含-排除原则所涉及的。他们也不计算一组集合的成对交集的长度总和,这是问题中明确要求的更具体的子问题。

标签: math haskell functional-programming scheme set


【解决方案1】:

这是使用 Scheme 的一种可能方式。我做了以下函数来创建量化

#lang racket

(define (quantification next test op e)
  {lambda (A B f-terme)
    (let loop ([i A] [resultat e])
      (if [test i B] 
          resultat 
          (loop (next i) (op (f-terme i) resultat)) ))})

使用此功能,您可以创建和、积、广义并集和广义交集。

;; Arithmetic example
(define sumQ (quantification add1 > + 0))
(define productQ (quantification add1 > * 1))

;; Sets example with (require 
(define (unionQ set-of-sets) 
  (let [(empty-set (set))
        (list-of-sets (set->list set-of-sets))
        ]
    ((quantification cdr eq? set-union empty-set) list-of-sets
                                                  '() 
                                                  car)))
(define (intersectionQ set-of-sets) 
  (let [(empty-set (set))
        (list-of-sets (set->list set-of-sets))
        ]
    ((quantification cdr eq? set-intersect (car list-of-sets)) (cdr list-of-sets)
                                                               '() 
                                                               car)))

这样就可以了

(define setA2 (set 'a 'b))
(define setA5 (set 'a 'b 'c 'd 'e))
(define setC3 (set 'c 'd 'e))
(define setE3 (set 'e 'f 'g))
(unionQ (set setA2 setC3 setE3))
(intersectionQ (set setA5 setC3 setE3))

我在 Haskell 中做类似的事情

module Quantification where

quantifier next test op = 
    let loop e a b f = if (test a b) 
                       then e 
                       else loop (op (f a) e) (next a) b f 
    in loop

quantifier_on_integer_set = quantifier (+1) (>)
sumq = quantifier_on_integer_set (+) 0
prodq = quantifier_on_integer_set (*) 1

但我永远不会走得更远......也许你可以从这个开始。

【讨论】:

    【解决方案2】:

    简单实现包含-排除原则所需的核心功能是生成索引集的所有 k 元素子集。使用列表,这是一个简单的递归:

    pick :: Int -> [a] -> [[a]]
    pick 0 _    = [[]]    -- There is exactly one 0-element subset of any set
    pick _ []   = []      -- No way to pick any nonzero number of elements from an empty set
    pick k (x:xs) = map (x:) (pick (k-1) xs) ++ pick k xs
        -- There are two groups of k-element subsets of a set containing x,
        -- those that contain x and those that do not
    

    如果 pick 不是一个本地函数,其调用 100% 在您的控制之下,您应该添加检查 Int 参数永远不会为负(您可以使用 Word 作为该参数,然后它内置于方式)。 如果k 较大,检查要从中选择的列表的长度可以防止大量无用的递归,因此最好从一开始就构建它:

    pick :: Int -> [a] -> [[a]]
    pick k xs = choose k (length xs) xs
    
    choose :: Int -> Int -> [a] -> [[a]]
    choose 0 _ _     = [[]]
    choose k l xs
        | l < k      = []    -- we want to choose more than we have
        | l == k     = [xs]  -- we want exactly as many as we have
        | otherwise  = case xs of
                         [] -> error "This ought to be impossible, l == length xs should hold"
                         (y:ys) -> map (y:) (choose (k-1) (l-1) ys) ++ choose k (l-1) ys
    

    则包含-排除公式变为

    inclusionExclusion indices
        = sum . zipWith (*) (cycle [1,-1]) $
            [sum (map count $ pick k indices) | k <- [1 .. length indices]]
    

    其中count list 计算[subset i | i &lt;- list] 的交集的元素数。当然,您需要一种有效的方法来计算它,或者直接找到联合的大小会更有效。

    还有很大的优化空间,并且有不同的方法可以做到这一点,但这是对原则的相当简短和直接的翻译。

    【讨论】:

    • 这应该是公认的答案。仅供参考,subset i 旨在代表问题中公式中的A_i(在包含-排除中,它们都是更大集合的子集)。
    【解决方案3】:

    在球拍中,您可能会使用列表推导:

    #lang racket
    
    (for*/sum ([i (in-range 1 5)]
               [j (in-range (add1 i) 5)])
        (* i j))
    

    【讨论】:

      【解决方案4】:

      在 Haskell 中,使用列表推导:

      Prelude> [(i,j) | i <- [1..4], j <- [i+1..4]]
      [(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)]
      Prelude> [i * j | i <- [1..4], j <- [i+1..4]]
      [2,3,4,6,8,12]
      Prelude> sum [i * j | i <- [1..4], j <- [i+1..4]]
      35
      

      第一行给出所有对 (i,j) 的列表,其中 1

      第二行给出 i*j 的列表,其中 1

      第三行给出这些值的总和:Σ1 i*j.

      【讨论】:

        猜你喜欢
        • 2010-09-06
        • 1970-01-01
        • 1970-01-01
        • 2015-02-27
        • 2012-11-02
        • 1970-01-01
        • 2021-09-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多