如果你有一个 NSMutableArray, 你怎么随机的随机因素?

(我有我自己回答这个,这是贴在下面,但我是新来的可可和我有兴趣知道,如果有一个更好的方式。)


更新:注意到@穆克什,作为iOS10+和mac os10.12+,还有一个 -[NSMutableArray shuffledArray] 方法,可以用来洗牌。看看 https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc 对于细节。(但注意,这创造了一个新阵列,而不是洗的元素的地方。)

有帮助吗?

解决方案

你不需要swapObjectAtIndex方法。 exchangeObjectAtIndex:withObjectAtIndex: 已经存在。

其他提示

我解决了这个通过增加一个类别NSMutableArray.

编辑: 去除不必要的方法感到答案由拉德.

编辑: 改变了 (arc4random() % nElements)arc4random_uniform(nElements) 谢谢你回答的格雷戈里Goltsov和评论意见miho和blahdiblah

编辑: 循环的改善,感谢意见罗恩

编辑: 加入检查,一阵是不是空的,谢谢来见马赫什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

因为我可能没有意见,我想我会做出贡献的一个全面响应。我修改教授约翰逊的执行对我的项目在一定数量的方式(真的想让它尽可能简洁),其中之一 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

从iOS10您可以使用新的 shuffled API:

https://developer.apple.com/reference/foundation/nsarray/1640855-shuffled

let shuffledArray = array.shuffled()

一个稍有改进和简明的解决方案(相比的顶答)。

算法是相同的,是文献中所述的"Fisher-耶茨洗牌".

在目标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

在迅速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))))
        }
    }
}

在迅速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])
        }
    }
}

注: 一个更简洁的解决方案迅速,可以从iOS10使用 GameplayKit.

注: 一种算法,对于不稳定的洗牌(与所有职位,被迫改变,如果数>1)也可用

这是最简单和最快的方式来洗牌NSArrays或NSMutableArrays (对象的难题是一个NSMutableArray,它包含难题的对象。我加入 拼图的对象可变指数,这表明最初的位置阵列)

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
  • NSOrderedSame=0
  • NSOrderedDescending=1

有一个很好的受欢迎的图书馆,这种方法,因为它的一部分,称为 SSToolKit在。.文件NSMutableArray+SSToolkitAdditions.h包含洗牌的方法。你可以用它。在此之中,似乎有很多实用的东西。

主页这是图书馆 在这里,.

如果你用这个,你的代码会是这样的:

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

该图书馆还有一个Pod(见CocoaPods)

从iOS10,可以使用 shuffled() 从GameplayKit.这是一个帮助列在迅速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())
    }
}

如果元素有重复。

例如阵列:一个B B或B一个

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

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;
                }
            }
        }
    }
}
NSUInteger randomIndex = arc4random() % [theArray count];

教授约翰逊的答案 是相当不错,但它不是完全随机的。

鉴于一系列的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];
       }
   }
}

编辑: 这不是正确的。 用于参考目的,我没有删除这一员额。看到的评论意见的原因,为什么这种方法是不正确的。

简单的代码这里:

- (NSArray *)shuffledArray:(NSArray *)array
{
    return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        if (arc4random() % 2) {
            return NSOrderedAscending;
        } else {
            return NSOrderedDescending;
        }
    }];
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top