C# 什么是洗牌NSMutableArray的最佳方式?

更新时间:2023-12-26 下载TXT文档 下载Word文档

如果你有一个NSMutableArray,你如何随机地改变元素?

(我有自己的答案,这是张贴在下面,但我是新的可可,我有兴趣知道是否有更好的方法。)

更新:正如@mukesh所指出的,从iOS 10+和MacOS 10.12+开始,有一个-[NSMutableArray shuffledArray]方法可用于无序播放。请参阅https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffedarray?语言=objc了解详细信息。(但请注意,这将创建一个新的数组,而不是将元素重新排列到位。)

  • 看看这个问题:关于你的洗牌算法的简单洗牌的现实问题。
  • 以下是swift:iosdeveloperTips.com/swift code/swift shuffle array type.htm‌&8203;l中的一个实现
  • 目前最好的是Fisher Yates:for (NSUInteger i = self.count; i > 1; i--) [self exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
  • 伙计们,从苹果给出的iOS 10++新的随机播放阵列概念来看这个答案。
  • 现有的API的问题是它返回一个新的Array,该地址指向内存中的新位置。

我通过向nsmutablearray添加一个类别来解决这个问题。

编辑:删除了不必要的方法,感谢LADD的回答。

编辑:由于Gregory Goltsov的回答和Miho和Blahdiblah的评论,(arc4random() % nElements)改为arc4random_uniform(nElements)

编辑:循环改进,感谢Ron的评论

编辑:添加检查数组是否不为空,这要感谢Mahesh Agrawal的评论。

//  NSMutableArray_Shuffling.h

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#include <Cocoa/Cocoa.h>
#endif

// This category enhances NSMutableArray by providing
// methods to randomly shuffle the elements.
@interface NSMutableArray (Shuffling)
- (void)shuffle;
@end


//  NSMutableArray_Shuffling.m

#import"NSMutableArray_Shuffling.h"

@implementation NSMutableArray (Shuffling)

- (void)shuffle
{
    NSUInteger count = [self count];
    if (count <= 1) return;
    for (NSUInteger i = 0; i < count - 1; ++i) {
        NSInteger remainingCount = count - i;
        NSInteger exchangeIndex = i + arc4random_uniform((u_int32_t )remainingCount);
        [self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
    }
}

@end
  • 很好的解决方案。是的,正如willc2所提到的,用arc4random()替换random()是一个很好的改进,因为不需要播种。
  • @杰森:有时候(例如在测试时),能够提供种子是一件好事。克里斯托弗:很好的算法。这是fisher-yates算法的一个实现:en.wikipedia.org/wiki/fisher-yates-shuffle
  • 一般情况下,最好使用arc4random(),而不是random()。数字的质量要好得多,不需要播种。
  • 一个非常小的改进:在循环的最后一次迭代中,i==count-1。这不意味着我们在索引i处与它本身交换对象吗?我们可以调整代码以总是跳过最后一次迭代吗?
  • 在循环之前,我还添加了一个布尔值"bool shuffled=false"。在循环过程中,我在检查"如果(我!=n)无序排列=真;""在循环之后,我要检查事情是否被洗牌了:"如果(!洗牌[自洗牌]"因为我有很多3个项目的阵列,没有定期洗牌。
  • @托马斯,只有六种方法可以订购一个3项数组,所以你会希望事情在大约1/6的时间里"没有缓冲"出来。如果你在任何时候重新洗牌,那么结果就不是很随机的。
  • 是的,但我的东西总是需要洗牌。
  • 你是否认为只有当结果与最初上升的那一边相反时,硬币才会被翻转?
  • 我更喜欢使用arc4random_uniform(nelements)而不是arc4random()%nelements。也使用EnumerateObjects而不是for。示例:sourceddrop.net/uy787e411c71
  • 我用过这个方法,但是如何调用这个方法我找不到解决方案
  • 这种混乱是微妙的偏见。用arc4random_uniform(nElements)代替arc4random()%nElements。有关更多信息,请参阅arc4random手册页和模块偏差的解释。
  • 在循环中实例化nsinteger:s?我不会那么做的…另外,我也经常这样做。
  • @jonny NSInteger只是intlong的typedef。"实例化"它们并不昂贵,编译器可能会对它们进行优化。
  • 您能不能添加一个if子句来检查计数是否大于0。因为如果数组是空的,并且从某个地方调用它,那么它将导致问题。另外,还可以安全地检查交换索引是否超出数组边界,以及是否为负。
  • @Maheshagrawal谢谢。我加了一张计数小于1的支票,这确实会造成问题。我认为不需要对交换索引进行额外的检查,因为它不可能超出界限,因为它是如何初始化的。
  • @克里斯托弗约翰逊感谢你的加入。你是对的。实际上,在开始调试时,它给了我一些越界值,但后来我明白这是因为小于0。不需要越界检查,因为您已经添加了检查。
  • @克里斯托弗约翰逊,最后一次编辑应该是埃多克斯1〔8〕吗?为什么会有人用一个元素洗牌一个数组?

你不需要swapobjectatindex方法。ExchangeObjectAtindex:WithObjectAtindex:已存在。

既然我还不能发表评论,我想我会给出一个完整的回应。我以多种方式修改了Kristopher Johnson对我的项目的实现(真的试图使其尽可能简洁),其中之一是arc4random_uniform(),因为它避免了模块偏差。

// NSMutableArray+Shuffling.h
#import <Foundation/Foundation.h>

/** This category enhances NSMutableArray by providing methods to randomly
 * shuffle the elements using the Fisher-Yates algorithm.
 */
@interface NSMutableArray (Shuffling)
- (void)shuffle;
@end

// NSMutableArray+Shuffling.m
#import"NSMutableArray+Shuffling.h"

@implementation NSMutableArray (Shuffling)

- (void)shuffle
{
    NSUInteger count = [self count];
    for (uint i = 0; i < count - 1; ++i)
    {
        // Select a random element between i and end of array to swap with.
        int nElements = count - i;
        int n = arc4random_uniform(nElements) + i;
        [self exchangeObjectAtIndex:i withObjectAtIndex:n];
    }
}

@end
  • 请注意,在通过循环的每次迭代中,您都要调用两次[self count](一个属性getter)。我认为把它移出循环是值得的,失去简洁性。
  • 这就是为什么我仍然更喜欢[object method]而不是object.method:人们往往会忘记后者不如访问结构成员便宜,它附带了方法调用的成本…循环中非常糟糕。
  • 感谢您的更正-出于某种原因,我错误地认为Count被缓存了。更新了答案。
  • 没关系,但你把一些uint类型混在一起了…

从iOS 10可以使用新的shuffledAPI:

https://developer.apple.com/reference/foundation/nsarray/1640855-无序播放

let shuffledArray = array.shuffled()

  • 我有我的数组,想创建一个新的shufflearray。我该如何实现目标-C?
  • 江户十一〔四〕号

一个稍微改进和简洁的解决方案(与最重要的答案相比)。

该算法与文献中描述的"Fisher-Yates shuffle"相同。

在目标C中:

@implementation NSMutableArray (Shuffle)
// Fisher-Yates shuffle
- (void)shuffle
{
    for (NSUInteger i = self.count; i > 1; i--)
        [self exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
}
@end

在Swift 3.2和4.x中:

extension Array {
    /// Fisher-Yates shuffle
    mutating func shuffle() {
        for i in stride(from: count - 1, to: 0, by: -1) {
            swapAt(i, Int(arc4random_uniform(UInt32(i + 1))))
        }
    }
}

在Swift 3.0和3.1中:

extension Array {
    /// Fisher-Yates shuffle
    mutating func shuffle() {
        for i in stride(from: count - 1, to: 0, by: -1) {
            let j = Int(arc4random_uniform(UInt32(i + 1)))
            (self[i], self[j]) = (self[j], self[i])
        }
    }
}

注:使用GameplayKit可以从IOS10获得更简洁的swift解决方案。

注:还提供了一种不稳定洗牌的算法(如果计数>1,则强制更改所有位置)。

  • 这和克里斯托弗·约翰逊的算法有什么区别?
  • @尤利亚诺弗雷,最初,克里斯托弗·约翰逊的代码不是最佳的,我改进了他的答案,然后重新编辑,添加了一些无用的初始检查。我更喜欢我简洁的写作方式。该算法与文献中描述的"Fisher-Yates shuffle"相同。

这是最简单和最快的方法来洗牌NSarray或NSmutableArrays(对象拼图是一个非可变数组,它包含拼图对象。我已添加到指示数组中初始位置的益智对象变量索引) 短码网:DuanMa.NET

int randomSort(id obj1, id obj2, void *context ) {
        // returns random number -1 0 1
    return (random()%3 - 1);    
}

- (void)shuffle {
        // call custom sort function
    [puzzles sortUsingFunction:randomSort context:nil];

    // show in log how is our array sorted
        int i = 0;
    for (Puzzle * puzzle in puzzles) {
        NSLog(@" #%d has index %d", i, puzzle.index);
        i++;
    }
}

日志输出:

 #0 has index #6
 #1 has index #3
 #2 has index #9
 #3 has index #15
 #4 has index #8
 #5 has index #0
 #6 has index #1
 #7 has index #4
 #8 has index #7
 #9 has index #12
 #10 has index #14
 #11 has index #16
 #12 has index #17
 #13 has index #10
 #14 has index #11
 #15 has index #13
 #16 has index #5
 #17 has index #2

您也可以将obj1与obj2进行比较,并决定要返回的内容可能的值是:

  • nsOrderedAscending=-1
  • nsOrderedName=0
  • nsOrderedDescending=1
  • 对于这个解决方案,也可以使用arc4random()或seed。
  • 这种洗牌是有缺陷的——正如微软最近提醒的那样:robweir.com/blog/2010/02/microsoft random browser ballot.htm&zwnj;&8203;l。
  • 同意,有缺陷,因为"排序需要一个自我一致的排序定义",正如那篇关于女士的文章所指出的那样,看起来很优雅,但事实并非如此。

从iOS 10,您可以使用游戏工具包中的nsarray shuffled()。以下是Swift 3中数组的助手:

import GameplayKit

extension Array {
    @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
    func shuffled() -> [Element] {
        return (self as NSArray).shuffled() as! [Element]
    }
    @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
    mutating func shuffle() {
        replaceSubrange(0..<count, with: shuffled())
    }
}

有一个很好的流行库,它有这个方法作为它的一部分,在GitHub中称为sstoolkit。文件nsmutablerray+sstoolkitadditions.h包含shuffle方法。你也可以用它。其中,似乎有很多有用的东西。

这个图书馆的主页在这里。

如果使用此代码,则代码如下:

#import <SSCategories.h>
NSMutableArray *tableData = [NSMutableArray arrayWithArray:[temp shuffledArray]];

这个图书馆也有一个豆荚(见椰子)

如果元素有重复。

例如阵列:A A A B或B B A A

唯一的解决办法是:A B A B A

sequenceSelected是一个NSmutableArray,它存储类obj的元素,这些元素是指向某个序列的指针。

- (void)shuffleSequenceSelected {
    [sequenceSelected shuffle];
    [self shuffleSequenceSelectedLoop];
}

- (void)shuffleSequenceSelectedLoop {
    NSUInteger count = sequenceSelected.count;
    for (NSUInteger i = 1; i < count-1; i++) {
        // Select a random element between i and end of array to swap with.
        NSInteger nElements = count - i;
        NSInteger n;
        if (i < count-2) { // i is between second  and second last element
            obj *A = [sequenceSelected objectAtIndex:i-1];
            obj *B = [sequenceSelected objectAtIndex:i];
            if (A == B) { // shuffle if current & previous same
                do {
                    n = arc4random_uniform(nElements) + i;
                    B = [sequenceSelected objectAtIndex:n];
                } while (A == B);
                [sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:n];
            }
        } else if (i == count-2) { // second last value to be shuffled with last value
            obj *A = [sequenceSelected objectAtIndex:i-1];// previous value
            obj *B = [sequenceSelected objectAtIndex:i]; // second last value
            obj *C = [sequenceSelected lastObject]; // last value
            if (A == B && B == C) {
                //reshufle
                sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy];
                [self shuffleSequenceSelectedLoop];
                return;
            }
            if (A == B) {
                if (B != C) {
                    [sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:count-1];
                } else {
                    // reshuffle
                    sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy];
                    [self shuffleSequenceSelectedLoop];
                    return;
                }
            }
        }
    }
}

  • 使用static可以防止在多个实例上工作:使用两个方法(一个主要方法执行无序排列并调用辅助方法)会更安全和可读,而辅助方法只调用自身而不重新无序排列。还有一个拼写错误。

克里斯托弗·约翰逊的回答很好,但并不是完全随机的。

给定一个由2个元素组成的数组,此函数总是返回逆数组,因为生成的是剩余索引的随机范围。更精确的shuffle()函数如下

- (void)shuffle
{
   NSUInteger count = [self count];
   for (NSUInteger i = 0; i < count; ++i) {
       NSInteger exchangeIndex = arc4random_uniform(count);
       if (i != exchangeIndex) {
            [self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
       }
   }
}
  • 我认为你所建议的算法是"简单的洗牌"。参见blog.codinghorry.com/the-danger-of-naivete。我认为如果只有两个元素,我的答案有50%的几率交换元素:当我为零时,arc4random_uniform(2)将返回0或1,因此第0个元素将与自身交换或与第1个元素交换。在下一个迭代中,当我是1时,arc4random(1)将始终返回0,并且ith元素将始终与自身交换,这是低效的,但不是不正确的。(也许循环条件应该是i < (count-1)。)
NSUInteger randomIndex = arc4random() % [theArray count];

  • 或者,如果您所支持的Mac OS X或iOS版本上有可用的arc4random_uniform([theArray count])会更好。
  • 我给你这样的号码会重复。

编辑:这不正确。出于参考目的,我没有删除这篇文章。请参阅有关此方法不正确的原因的注释。

此处为简单代码:

- (NSArray *)shuffledArray:(NSArray *)array
{
    return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        if (arc4random() % 2) {
            return NSOrderedAscending;
        } else {
            return NSOrderedDescending;
        }
    }];
}

  • 这种混乱是有缺陷的–robweir.com/blog/2010/02/microsoft random browser ballot.htm&zwnj;&8203;l

以上就是短码网小编为大家整理的《C# 什么是洗牌NSMutableArray的最佳方式?》相关内容,希望大家喜欢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若内容造成侵权/违法违规/事实不符,请将联系本站反馈,一经查实,立即处理!

C# 什么是洗牌NSMutableArray的最佳方式?》文档下载仅供参考学习,下载后请在24小时内删除。

转载注明出处:https://www.duanma.net/article/d384daa7377.html

回到顶部