简体   繁体   English

Haskell:查找等于n的元素的元组

[英]Haskell: finding tuples of elements that equal n

I am trying to write a function in Haskell that takes a list of integers and an integer n and finds all tuples that equal n. 我试图在Haskell中编写一个函数,该函数接受一个整数列表和一个整数n并找到等于n的所有元组。 So far I have an implementation that works 到目前为止,我有一个可行的实现

tuplesum :: (Eq b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = [(x1,x2) | x1 <- xs, x2 <- xs, x1 + x2 == n, x1 <= x2]

so if I gave this function the input 所以如果我给这个功能输入

tuplesum [5,1,4,0,5,6,9] 10

the output is [(5,5),(5,5),(1,9),(4,6),(5,5),(5,5)] However, I have 4 duplicates of the (5,5) solution. 输出为[(5,5),(5,5),(1,9),(4,6),(5,5),(5,5)]但是,我有(5的4个重复项,5)解决。 I would like the function to output [(5,5),(1,9),(4,6)] but I cant figure out how to constrain tuples that have the same integers without removing it as a solution entirely. 我希望该函数输出[(5,5),(1,9),(4,6)]但我无法弄清楚如何约束具有相同整数的元组而不将其作为解决方案完全删除。

Symmetry breaking in tuple generation 元组生成中的对称性破坏

I have the impression that you are looking for a way to select two elements out of the list such that x1 is always located before x2 . 我的印象是,您正在寻找一种从列表中选择两个元素的方法,以使x1始终位于x2 之前

A common way to always let x2 iterate over the remainder of the list is by using tails :: [a] -> [[a]] . 始终让x2遍历列表其余部分的常用方法是使用tails :: [a] -> [[a]] For a list, tails will generate a list of all tails of the list, starting with the list itself. 对于列表, tails将生成列表的所有尾部的列表,从列表本身开始。 For example: 例如:

Prelude Data.List> tails [1, 4, 2, 5]
[[1,4,2,5],[4,2,5],[2,5],[5],[]]

We can use this with pattern matching to select one element, and get a reference to the remaining element. 我们可以将其与模式匹配一​​起使用,以选择一个元素,并获得对其余元素的引用。 For example: 例如:

import Data.List(tails)

tuplesum :: (Eq b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = [(x1,x2) | (x1:x2s) <- tails xs, x2 <- x2s, x1 + x2 == n]

Note that it is still possible to obtain duplicates here, for example if 5 would occur three times in the list, since in that case x1 can select the first 5 , and then x2 can select the second 5 as well as the last one. 请注意,这里仍然有可能获得重复项,例如,如果5次出现在列表中三次,因为在这种情况下x1可以选择前5 ,然后x2可以选择第二个5和最后一个。 We can make use of a uniqness filter like nub :: Eq a => [a] -> [a] for this: 为此,我们可以使用诸如nub :: Eq a => [a] -> [a]的唯一性过滤器:

import Data.List(nub, tails)

tuplesum :: (Eq b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = nub [(x1,x2) | (x1:x2s) <- tails xs, x2 <- x2s, x1 + x2 == n]

Note that it is however still better to use tails here, since it will increase performance, since we will simply generate a smaller amount of duplicates in the first place. 但是请注意,此处最好使用tails ,因为这样可以提高性能,因为首先我们只是生成少量重复项。

Using a hashset to obtain the "other" element 使用哈希集获取“其他”元素

The above algorithm is still O(n 2 ) , and not very fast. 上面的算法仍然是O(n 2 ,并且不是很快。 We can however solve the problem the other way around: we can first construct a HashSet of the elements, and the for each element x1 , check if n - x1 is a member, like: 但是,我们可以用另一种方法解决问题:首先可以构造元素的HashSet ,对于x1每个元素,检查n - x1是否为成员,例如:

import Data.Hashable(Hashable)
import Data.HashSet(fromList, member)

tuplesum :: (Ord b, Hashable b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = nub [(x1,x2) | x1 <- xs, let x2 = n-x1, x1 <= x2, member x2 hs]
    where hs = fromList xs

But the runtime is still O(n 2 ) because of the nub , we can however use a hashNub :: (Eq a, Hashable a) => [a] -> [a] here: 但是由于nub ,运行时仍然是O(n 2 ,但是我们可以在这里使用hashNub :: (Eq a, Hashable a) => [a] -> [a]

 hashNub :: (Eq a, Hashable a) => [a] -> [a] hashNub = go HashSet.empty where go _ [] = [] go s (x:xs) = if x `HashSet.member` s then go s xs else x : go (HashSet.insert xs) xs 

and then let it work with: 然后让它与:

import Data.Hashable(Hashable)
import Data.HashSet(fromList, member)

tuplesum :: (Ord b, Hashable b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = hashNub [(x1,x2) | x1 <- xs, let x2 = n-x1, x1 <= x2, member x2 hs]
    where hs = fromList xs

Now it works in O(n log n) . 现在它可以在O(n log n)中工作

I just really liked your function tuplesum xs n = [(x1,x2) | x1 <- xs, x2 <- xs, x1 + x2 == n, x1 <= x2] 我真的很喜欢您的函数tuplesum xs n = [(x1,x2) | x1 <- xs, x2 <- xs, x1 + x2 == n, x1 <= x2] tuplesum xs n = [(x1,x2) | x1 <- xs, x2 <- xs, x1 + x2 == n, x1 <= x2] because it is a Cartesian product and it eliminates most symmetrical pairs that would otherwise constitute about half. tuplesum xs n = [(x1,x2) | x1 <- xs, x2 <- xs, x1 + x2 == n, x1 <= x2]因为它是笛卡尔积,并且消除了大多数对称对,否则它们将构成大约一半。 It gets the predicate matches so very well. 谓词匹配非常好。 The only problem remaining is the duplicate elements. 剩下的唯一问题是重复元素。 I had forgotten about this until recently and Pg. 直到最近我和Pg都忘记了这一点。 86 of Graham Hutton 's "Programming in Haskell" and his rmdups function. 格雷厄姆·赫顿Graham Hutton )的“在Haskell中编程 ”的86和他的rmdups功能。 What I like about his rmdups is that it neither depends on imports. 我喜欢他的rmdups ,因为它既不依赖进口。

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : filter (/= x) (rmdups xs)

Hutton's solution is wonderfully general and classically recursive. 赫顿的解决方案具有极好的通用性和经典递归性。 I did not want to post his solution here without adding something original so here is a list comprehension to eliminate duplicates of any data type including tuples. 我不想在不添加原始内容的情况下在这里发布他的解决方案,因此这里是一个列表理解功能,用于消除任何数据类型(包括元组)的重复项。

rmdups ls = [d|(z,d)<- zip [0..] ls, notElem d $ take z ls]

You can place either rmdups function in front of your function rmdups.tuplesum Your function eliminates most symmetrical pairs so rmdups does not. 您可以将rmdups函数放置在函数rmdups.tuplesum前面,函数可以消除大多数对称对,因此rmdups不会。

rmdups [(5,5),(5,5),(1,9),(4,6),(5,5),(5,5)]

[(5,5),(1,9),(4,6)] [(5,5),(1,9),(4,6)]

Or 要么

rmdups "abcabcdefdef" OR "abcdefabcdef"

"abcdef" “ABCDEF”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM