从列表中选取不同数字的对称配对



我有一个这样的列表:

[(2,3),(2,5),(2,7),(3,2),(3,4),(3,6),(4,3),(4,5),(4,7),(5,2),(5,4),(5,6),(6,3),(6,5),(6,7),(7,2),(7,4),(7,6)]

数字是从[2..7]开始的。我想取一个有对称对的集合。例如[(1,2(,(2,1(],但这两个数字在集合中不再使用。例如:

[(3,6),(6,3),(2,5),(5,2),(4,7),(7,4)]

我想首先把对称对放在一起,因为我认为这可能更容易处理,所以我创建了这个函数,它实际上创建了对,并将它们放在另一个列表中

g xs = [ (y,x):(x,y):[] | (x,y) <- xs ]

列表转向这个:

[[(3,2),(2,3)],[(5,2),(2,5)],[(7,2),(2,7)],[(2,3),(3,2)],[(4,3),(3,4)],[(6,3),(3,6)],[(3,4),(4,3)],[(5,4),(4,5)],[(7,4),(4,7)],[(2,5),(5,2)],[(4,5),(5,4)],[(6,5),(5,6)],[(3,6),(6,3)],[(5,6),(6,5)],[(7,6),(6,7)],[(2,7),(7,2)],[(4,7),(7,4)],[(6,7),(7,6)]]

然后,我希望从这里以某种方式删除重复。

我做了一个函数,将查看所有对的所有fst元素:

flatList xss = [ x | xs <- xss, (x,y) <- xs ]

与另一个函数一起使用以删除重复项。

h (x:xs) | (fst (head x)) `elem` (flatList xs) = h xs
| otherwise = (head x):(last x):(h xs)

这给了我列表

[(3,6),(6,3),(5,6),(6,5),(2,7),(7,2),(4,7),(7,4),(6,7),(7,6)]

其具有重复的编号。该函数只考虑列表列表中第一对的第一个元素,问题是当我也考虑第二对的第一元素(或第一对的第二元素(时:

h (x:xs) | (fst (head x)) `elem` (flatList xs) || (fst (last x)) `elem` (flatList xs) = h xs
| otherwise = (head x):(last x):(h xs)

我只得到这两双:

[(6,7(,(7,6(]

我发现问题在于,这种删除重复项的方法会抓住最后一个重复的元素,并且会使用数字列表,但不会使用对列表,因为它会错过需要获取的对。

有没有其他方法可以解决这个问题,或者我可以做一个修改?

在列表理解中使用2元组中的2元组可能更有意义,因为这样可以更容易地进行模式匹配,因此"by contract"强制执行有两个项的事实。因此,我们可以构造包含以下2元组的2元组:

g :: Eq a => [(a, a)] -> [((a, a), (a, a))]
g xs = [ (t, s) | (t@(x,y):ts) <- tails xs, let s = (y, x), elem s ts ]

在这里,elem s ts检查"交换的"2元组是否出现在列表的其余部分中。

然后我们仍然需要过滤元素。我们可以使用一个函数,该函数对迄今为止获得的项目使用累加器:

h :: Eq a => [((a, a), (a, a))] -> [(a, a)]
h = go []
where go _ [] = []
go seen ((t@(x, y), s):xs)
| notElem x seen && notElem y seen = t : s : go (x:y:seen) xs
| otherwise = go seen xs

对于给定的样本输入,我们得到:

Prelude Data.List> (h . g) [(2,3),(2,5),(2,7),(3,2),(3,4),(3,6),(4,3),(4,5),(4,7),(5,2),(5,4),(5,6),(6,3),(6,5),(6,7),(7,2),(7,4),(7,6)]
[(2,3),(3,2),(4,5),(5,4),(6,7),(7,6)]

在读了几遍你的问题后,我为你的问题找到了一个优雅的解决方案。如果你有一个没有任何重复数字的配对列表,你可以很容易地得到交换配对的列表,从而解决你的问题。因此,您的问题可以简化为给定一个列表,使用每个数字获得所有对的列表

对于给定的列表,有许多解决方案,例如:对于[1,2,3,4],有效的解决方案是:[(2,4),(4,2),(1,3),(3,1)][(2,3),(3,2),(1,4),(4,1)]等。这里的方法是:

  • 如果原始列表(比如[1,4,3,2](
  • 为每一半选择一个元素,并将它们配对在一起(为了简单起见,您也可以选择连续的元素(
  • 对于每一对,创建一个交换的对,并将它们放在一起

这样做,您最终会得到一个非重复对数及其对称数的列表。更重要的是,循环所有的置换,你可以得到你问题的所有解决方案。

import Data.List (permutations, splitAt)
import Data.Tuple (swap)
-- This function splits a list by the half of the length
splitHalf :: [a] -> ([a], [a])
splitHalf xs = splitAt (length xs `quot` 2) xs
-- This zip a pair of list into a list of pairs 
zipHalfs :: ([a], [a]) -> [(a,a)]
zipHalfs (xs, ys) = zip xs ys
-- Given a list of tuples, creates a larger list with all tuples and all swapped tuples
makeSymetrics :: [(a,a)] -> [(a,a)]
makeSymetrics xs = foldr (t l -> t:(swap t):l) [] xs
-- This chain all of the above. 
-- Take all permutations of xs >>> for each permutations >>> split it in two >>> zip the result >>> make swapped pairs
getPairs :: [a] -> [[(a,a)]]
getPairs xs =  map (makeSymetrics . zipHalfs . splitHalf) $ permutations xs
>>> getPairs [1,2,3,4]
[[(1,3),(3,1),(2,4),(4,2)],[(2,3),(3,2),(1,4),(4,1)] ....

最新更新