Haskell中的CycleSort - 麻烦

时间:2018-05-08 14:30:53

标签: algorithm sorting haskell cycle-sort

我正在尝试在Haskell中创建一个CycleSort,这是我的任务,但没有人让我明白如何使用Haskell,我已经多次尝试过,尝试从其他语言“翻译”代码,但它没有不行。我到处寻找它,但什么都没有。如果有人帮我解决这个问题,我会非常非常感激。 Java中有CycleSort的代码。

// Function sort the array using Cycle sort
public static void cycleSort(int arr[], int n)
{
    // count number of memory writes
    int writes = 0;

    // traverse array elements and put it to on
    // the right place
    for (int cycle_start = 0; cycle_start <= n - 2; cycle_start++) {
        // initialize item as starting point
        int item = arr[cycle_start];

        // Find position where we put the item. We basically
        // count all smaller elements on right side of item.
        int pos = cycle_start;
        for (int i = cycle_start + 1; i < n; i++)
            if (arr[i] < item)
                pos++;

        // If item is already in correct position
        if (pos == cycle_start)
            continue;

        // ignore all duplicate elements
        while (item == arr[pos])
            pos += 1;

        // put the item to it's right position
        if (pos != cycle_start) {
            int temp = item;
            item = arr[pos];
            arr[pos] = temp;
            writes++;
        }

        // Rotate rest of the cycle
        while (pos != cycle_start) {
            pos = cycle_start;

            // Find position where we put the element
            for (int i = cycle_start + 1; i < n; i++)
                if (arr[i] < item)
                    pos += 1;

            // ignore all duplicate elements
            while (item == arr[pos])
                pos += 1;

            // put the item to it's right position
            if (item != arr[pos]) {
                int temp = item;
                item = arr[pos];
                arr[pos] = temp;
                writes++;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:4)

不要试图翻译,而是转到wikipedia's definition

  

算法

     

给定元素a,我们可以找到它将在其中出现的索引   通过简单计算整个列表中的元素数量来排序列表   小于a。现在

     
      
  1. 如果元素已经处于正确位置,则不执行任何操作。
  2.   
  3. 如果不是,我们会将其写入预定位置。那个位置是   居住在不同的元素b,然后我们必须移动到它   正确的位置。这个过程将元素置换到正确的位置   位置继续,直到元素移动到原始位置   一个。这样就完成了一个循环。
  4.         

    对每个元素重复此过程,只需一次写入即可对列表进行排序   当且仅当元素尚未处于正确位置时才进行操作。   计算正确的位置需要每个单独的O(n)时间   元素,从而产生二次时间算法,写入次数   操作最小化。

         

    实施

         

    要从上面的大纲创建工作实现,   需要解决两个问题:

         
        
    1. 在计算正确的位置时,我们必须确保不要   重复计算周期的第一个元素。
    2.   
    3. 如果存在重复元素,我们可以尝试移动元素a   到了正确的位置,这个位置恰好是一个人居住的地方。   简单地交换这些将导致算法无限循环。   相反,我们必须在任何重复之后插入元素。
    4.   

在haskell中实现cycleSort,我们的第一个问题应该是应该做什么 cycleSort的类型是什么?

正常 sort 类型为sort :: Ord a => [a] -> [a],但这不适用于cycleSort。 循环排序是一种就地算法,因此排序列表毫无意义。相反, 我们要对mutable vector进行排序。

cycleSort :: Ord a => MVector s a -> MVector s a

但这种类型并不完全正确。 MVector的操作并不纯粹 - 他们 返回某些monad中的操作,通常为ST sIO。所以这种类型需要一个 小调整。

我们将返回一个将对其进行排序的操作,而不是返回已排序的向量 执行时可变向量:

cycleSort :: (PrimMonad m, Ord a) => MVector (PrimState m) a -> m ()

PrimMonad m只是意味着m可以构造改变向量的动作, PrimState m用于将MVector与此特定monad绑定。

为了与其他实现进行比较,我们可能需要计算数量 写道:

cycleSort :: (PrimMonad m, Ord a) => MVector (PrimState m) a -> m Int

所以现在我们可以研究算法本身。

由于这是一项任务,我不会为您提供解决方案,但这里是 一些有用的功能:

  • Data.Vector.Mutable.length :: MVector s a -> Int获取可变载体的长度
  • Data.Vector.Mutable.read :: PrimMonad m => MVector (PrimState m) a -> Int -> m a读取可变向量中索引的值
  • Data.Vector.Mutable.write :: PrimMonad m => MVector (PrimState m) a -> Int -> a -> m ()在可变向量中的索引处写入值

有关使用的示例,这是一个反转MVector的函数:

import Control.Monad (when)
import Control.Monad.Primitive (PrimMonad, PrimState)
import qualified Data.Vector.Mutable as MV

reverseMVector :: PrimMonad m => MV.MVector (PrimState m) a -> m ()
reverseMVector v = loop 0 (MV.length v - 1) where
  loop lo hi = when (lo < hi) $ do
    a <- MV.read v lo
    b <- MV.read v hi
    MV.write v lo b
    MV.write v hi a
    loop (lo+1) (hi-1)

有许多先进的技术可以使解决方案更漂亮,但是 递归函数(如上面的loop)就足够了。

例如,可以翻译

// ignore all duplicate elements
while (item == arr[pos])
    pos += 1;

作为

-- ignore all duplicate elements
let skipDupes pos = do
      jtem <- MV.read v pos
      if item == jtem
        then skipDupes (pos + 1)
        else return pos
pos <- skipDupes pos

如果要在各种输入上测试代码,请使用 Data.Vector.thaw / Data.Vector.freezeVector a与...进行转换 MVector s a

当您遇到问题时,请发布您目前为止的代码以及您遇到的错误 碰上了。