【问题标题】:C#: Good/best implementation of Swap methodC#:Swap 方法的良好/最佳实现
【发布时间】:2010-10-07 20:28:56
【问题描述】:

我读过这个post about card shuffling 并且在许多洗牌和排序算法中,您需要交换列表或数组中的两个项目。但是好的高效的 Swap 方法是什么样的呢?

假设T[]List<T>。您将如何最好地实现一种在这两个项目中交换两个项目的方法?

Swap(ref cards[i], ref cards[n]);   // How is Swap implemented?

【问题讨论】:

    标签: c# sorting shuffle


    【解决方案1】:

    好吧,您发布的代码 (ref cards[n]) 只能与数组(而不是列表)一起使用 - 但您可以简单地使用(其中 foobar 是两个值):

    static void Swap(ref int foo, ref int bar) {
        int tmp = foo;
        foo = bar;
        bar = tmp;
    }
    

    或者可能(如果你想要原子):

    Interlocked.Exchange(ref foo, ref bar);
    

    不过,就我个人而言,我不认为我会为交换方法而烦恼 - 直接执行即可;这意味着您可以使用(对于列表或数组):

    int tmp = cards[n];
    cards[n] = cards[i];
    cards[i] = tmp;
    

    如果您真的想编写一个适用于列表数组的交换方法,则必须执行以下操作:

    static void Swap(IList<int> list, int indexA, int indexB)
    {
        int tmp = list[indexA];
        list[indexA] = list[indexB];
        list[indexB] = tmp;
    }
    

    (使这个通用化是微不足道的) - 但是,在数组上工作的原始“内联”版本(即不是方法)会更快。

    【讨论】:

    • 重新数组 - 你没有传递数组 - 你正在传递元素。
    • 如果这些是整数,按值交换不是更好吗?
    • int 只是作为一个例子。可能应该在问题中澄清这一点......
    • 我假设 JIT 编译器将内联通用交换,因此我认为没有性能理由选择“内联”方法而不是通用扩展。
    • 我知道这是一个非常古老的问题,但您能否详细说明为什么泛型方法会比内联版本慢?这仍然准确吗?
    【解决方案2】:

    一个好的交换是你不交换内容的地方。在 C/C++ 中,这类似于交换指针而不是交换内容。这种交换方式很快并且带有一些异常保证。不幸的是,我的 C# 太生锈了,无法将其放入代码中。对于简单的数据类型,这种风格并没有给你太多。但是,一旦您习惯并不得不处理更大(更复杂)的物体,它就可以挽救您的生命。

    【讨论】:

    • 但对于 int[] 或 List,内容要么相同(x86),要么是大小的一半(x64)。在这种情况下,交换内容。
    【解决方案3】:

    这个呢? 它是交换方法的通用实现。 Jit 将为您的封闭类型创建编译版本,因此您不必担心性能!

    /// <summary>
    /// Swap two elements
    /// Generic implementation by LMF
    /// </summary>
    public static void Swap<T>(ref T itemLeft, ref T itemRight) {
        T dummyItem = itemRight;
        itemLeft = itemRight;
        itemRight = dummyItem;
    }
    

    HTH 洛伦佐

    【讨论】:

      【解决方案4】:

      11 年后,我们有了元组......

      (foo, bar) = (bar, foo);
      

      【讨论】:

      • 欢迎来到 SO,@gaggleweed,感谢您的出色贡献!注意:OP 要求将交换功能隐藏在方法中,但您的代码直接内联进行交换。我可以理解为什么在方法中放置如此​​简单的单行代码似乎没有必要,但如果您是这样想的,最好在帖子中解释一下。或者更好的是,解释 包含一个在方法中包含它的版本,以防 OP 有充分的理由想要它。
      • 还值得注意的是,这不是线程安全的,但只有在交换超出本地范围的变量时才有意义。
      【解决方案5】:

      用途:

      void swap(int &a, int &b)
      {
          // &a != &b
          // a == b OK
          a ^= b;
          b ^= a;
          a ^= b;
          return;
      }
      

      我没有意识到我在 C# 部分。这是 C++ 代码,但它应该具有相同的基本思想。我相信 ^ 在 C# 中也是 XOR。看起来你可能需要“ref”(?)而不是&amp;。我不确定。

      【讨论】:

      • 有趣...那些 cmets 是什么意思?
      • (真的需要return;吗?)
      • return 不需要,你可以使用 ref 代替 & yes, +1
      • FWIW,维基百科声称这种方法实际上比通常的 buffer = a; a = b; b = buffer; 构造效率低。
      • 基准测试表明,在 C# 中,临时变量交换确实比 3 次 XOR 操作更快。
      【解决方案6】:

      您现在可以使用元组来完成此交换,而无需手动声明临时变量:

      static void Swap<T>(ref T foo, ref T bar)
      {
          (foo, bar) = (bar, foo)
      }
      

      【讨论】:

      • 为什么这被否决了?
      【解决方案7】:

      对于任何想知道的人,也可以使用扩展方法(.NET 3.0 和更高版本)进行交换。

      一般来说,似乎不可能说扩展方法“this”的值是 ref,所以你需要返回它并覆盖旧值。

      public static class GeneralExtensions {
          public static T SwapWith<T>(this T current, ref T other) {
              T tmpOther = other;
              other = current;
              return tmpOther;
          }
      }
      

      这个扩展方法可以这样使用:

      int val1 = 10;
      int val2 = 20;    
      val1 = val1.SwapWith(ref val2);
      

      【讨论】:

        猜你喜欢
        • 2017-07-26
        • 1970-01-01
        • 2014-10-11
        • 2011-01-12
        • 2012-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多