【问题标题】:How to solve Randomize in place algorithm in C#?如何在 C# 中解决 Randomize in place 算法?
【发布时间】:2014-08-18 10:46:00
【问题描述】:

我正在阅读算法简介 - 第三版,现在,我必须实现 RANDOMIZE-IN-PLACE 算法,该算法必须置换当前数组中的每个值。 本书提供的伪代码如下:

n = A.length
for i = 1 to n
swap A[i] with A[Random(i, n)]

我尝试在 C# 上实现该功能,但有时会收到 IndexOutOfRangeException(仅在某些情况下)。我曾经调试过算法,发现当

randomValue = array[randomNumber.Next(index, upperBound)];

index 等于array.Length - 1 并且upperBound 是array.Length - 1 (换句话说index 和upperBound 是相同的值并且.Next 看起来像这样.Next(9, 9) 例如),随机生成器能够产生数字10(下限/上限+ 1)正是我的array.Length。如果有人知道如何解决这将对我非常有帮助。再次感谢。这是我的 C# 代码。

namespace RandomizedAlgorithms
{

using System;

class RandomizeInPlace
{
    static void Main()
    {
        int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        Randomize(array);

        for (int index = 0; index <= array.Length - 1 ; index++)
        {
            Console.WriteLine(array[index]);
        }
    }

    private static void Randomize(int[] array)
    {
        Random randomNumber = new Random();
        int swapVariable = 0;
        int randomValue = 0;
        int upperBound = array.Length - 1;

        for (int index = 0; index <= array.Length - 1 ; index++)
        {
            randomValue = array[randomNumber.Next(index, upperBound)];
            swapVariable = array[randomValue];
            array[randomValue] = array[index];
            array[index] = swapVariable;
        }
    }
}

}

【问题讨论】:

  • @MitchWheat 是 Fisher-Yates,如果我没记错的话
  • 您不需要将最后一个值与自身交换。
  • 但是根据伪代码,你希望 .Next 像 (currentPosition, TotalAmountOfItems) :/
  • @NahuelIanni 注意upperBound = array.Length - 1,本质上是TotalAmountOfItems
  • 请注意Random.Next(int) 的上限是独占的。 'The exclusive upper bound of the random number to be generated.' (msdn.microsoft.com/en-us/library/zd1bc8e5%28v=vs.110%29.aspx)。这意味着您应该使用int upperBound = array.Length;

标签: c# arrays algorithm


【解决方案1】:

您需要存储要交换的元素的随机索引,而不是数组值本身:

private static void Randomize(int[] array)
{
    Random randomNumber = new Random();
    int swapVariable = 0;
    int randomIndex;                // <-- renamed this
    int upperBound = array.Length;  // <-- See the comments

    for(int index = 0; index < array.Length; index++)
    {
        // Note: besides the minor changes above, the real fix is removing array[...]
        randomIndex = randomNumber.Next(index + 1, upperBound);
        swapVariable = array[randomIndex];
        array[randomIndex] = array[index];
        array[index] = swapVariable;
    }
}

我已尽可能少地更改您的代码,其他改进也是可能的。请注意,此版本允许元素与自身“交换”,这可能是也可能不是您想要的。

【讨论】:

  • 正如 stefan 在 cmets 中提醒我们的那样,upperBound 也需要更新为 array.Length,否则最后一项将永远不会被打乱(因为它永远不会出现在随机数生成器上)。此外,您的 randomIndex 之前应该有一个 var 以确保正确性。
  • 谢谢@Chris,我已经更新了代码。现在看起来好点了吗?我个人还有其他几项改进,但我想保持接近 OP 的代码。
  • 现在看起来很合适(我已经加了你,但害怕你不再从我这里得到任何东西;-))。
【解决方案2】:

您通过变量randomValue 使用数组值作为索引:

        randomValue = array[randomNumber.Next(index, upperBound)];
        swapVariable = array[randomValue];

您的数组值从 1 变为 10:

    int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

但是 c# 中的数组是从 0 开始的。将数组值从 0 更改为 9:

    int[] array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

更新

好的,正如评论者所指出的,您使用的是Fisher–Yates shuffle。这是该算法的直接翻译。请注意 Random.Next(int minValue, int maxValue) 的参数中奇怪的不对称性,它返回 (minValue &lt;= value &lt; maxValue)

public interface IRandomizer
{
    void RandomizeInPlace<T>(T[] array);
}

public class FisherYatesShuffle : IRandomizer 
{
    readonly Random randomNumber = new Random();

    public void RandomizeInPlace<T>(T[] array)
    {
        // http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm , second version.
        for (int i = 0; i < array.Length; i++)
        {
            // http://msdn.microsoft.com/en-us/library/2dx6wyd4%28v=vs.110%29.aspx
            var j = randomNumber.Next(i, array.Length); // i = inclusive lower bound; array.Length = The exclusive upper bound of the random number returned
            array.Swap(i, j);
        }
    }
}

public static class ListHelper
{
    public static void Swap<T>(this T[] list, int i, int j)
    {
        if (list == null)
            throw new ArgumentNullException();
        if (i != j)
        {
            T temp = list[i];
            list[i] = list[j];
            list[j] = temp;
        }
    }
}

【讨论】:

  • @J0HN - randomValue 被用作数组索引。它是数组中的一个值。
  • 嗯?为什么随机化算法会依赖于您正在洗牌的数组的
  • @CompuChip - 我不知道,我不熟悉算法。我只是指出可能导致IndexOutOfRangeException 的一行代码。
  • 对,我应该更彻底地阅读。但是,我没有投反对票。
  • 啊。我明白你现在的意思了。我猜您真正想要做的不是更改数组值,而是将索引也存储在临时变量中。编写一个只能对数组 { 0, ..., 9 } 进行洗牌的函数真的没有意义,不是吗?
猜你喜欢
  • 2018-09-03
  • 2011-06-23
  • 1970-01-01
  • 2014-10-13
  • 1970-01-01
  • 1970-01-01
  • 2022-12-11
  • 2013-04-11
  • 1970-01-01
相关资源
最近更新 更多