191

がある場合NSMutableArray、要素をランダムにシャッフルするにはどうすればよいですか?

(これについては私自身の回答があり、以下に掲載されていますが、私は Cocoa を初めて使用するので、より良い方法があるかどうか知りたいと思っています。)


更新: @Mukesh が指摘したように、iOS 10 以降および macOS 10.12 以降では、-[NSMutableArray shuffledArray]シャッフルに使用できる方法があります。詳細については、 https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objcを参照してください。(ただし、要素をその場でシャッフルするのではなく、新しい配列を作成することに注意してください。)

4

12 に答える 12

352

NSMutableArrayにカテゴリを追加することでこれを解決しました。

編集:ラッドによる回答のおかげで不要なメソッドを削除しました。

編集: GregoryGoltsovによる回答とmihoとblahdiblahによるコメントのおかげで変更(arc4random() % nElements)されましたarc4random_uniform(nElements)

編集:ロンによるコメントのおかげでループの改善

編集: 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
于 2008-09-11T14:20:06.423 に答える
74

swapObjectAtIndex メソッドは必要ありません。exchangeObjectAtIndex:withObjectAtIndex:既に存在します。

于 2008-09-11T21:03:24.333 に答える
39

まだコメントできないので、フルレスポンスで貢献したいと思いました。私は、プロジェクトのKristopher Johnsonの実装をいくつかの方法で変更しました(実際には可能な限り簡潔にしようとしています)。そのうちの1つは、モジュロバイアス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
于 2012-06-03T22:34:41.383 に答える
3

iOS 10から、 GameplayKit からNSArrayshuffled()を使用できます。Swift 3 の Array のヘルパーは次のとおりです。

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())
    }
}
于 2017-05-05T09:22:34.523 に答える
-1

クリストファー・ジョンソンの答えはかなりいいですが、完全にランダムではありません.

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];
       }
   }
}
于 2014-10-15T11:01:22.227 に答える
-1
NSUInteger randomIndex = arc4random() % [theArray count];
于 2012-04-26T13:42:45.977 に答える