سؤال

إذا كان لديك NSMutableArray, كيف يمكنك خلط العناصر عشوائيا ؟

(لدي إجابة عن هذا ، والتي يتم نشرها أدناه ، ولكن أنا جديد الكاكاو و أنا مهتم لمعرفة ما إذا كانت هناك طريقة أفضل.)


تحديث:كما أشار @موكيش من دائرة الرقابة الداخلية 10+ وماك 10.12+ هناك -[NSMutableArray shuffledArray] الطريقة التي يمكن استخدامها في خلط ورق اللعب.انظر https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc للحصول على التفاصيل.(ولكن لاحظ أن هذا يخلق مجموعة جديدة بدلا من خلط العناصر في المكان.)

هل كانت مفيدة؟

المحلول

لا تحتاج swapObjectAtIndex الأسلوب. exchangeObjectAtIndex:withObjectAtIndex: موجود بالفعل.

نصائح أخرى

لقد حل هذا عن طريق إضافة فئة NSMutableArray.

تحرير: إزالة لا لزوم لها طريقة شكرا للرد من قبل لاد.

تحرير: تغيرت (arc4random() % nElements) إلى arc4random_uniform(nElements) شكرا للرد من قبل غريغوري Goltsov و تعليقات ميهو و blahdiblah

تحرير: حلقة التحسن بفضل التعليق رون

تحرير: وأضاف أن تحقق مجموعة ليست فارغة, شكرا على التعليق من قبل ماهيش أغراوال

//  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

من دائرة الرقابة الداخلية 10 يمكنك استخدام جديدة shuffled API:

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

let shuffledArray = array.shuffled()

تحسن طفيف وموجزة الحل (مقارنة مع أعلى الأجوبة).

الخوارزمية هو نفسه كما هو موضح في الأدب "فيشر-ييتس المراوغة".

في الهدف-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.ح تحتوي على المراوغة الأسلوب.يمكنك استخدامه أيضا.بين هذا ، يبدو أن هناك طن من الأشياء المفيدة.

الصفحة الرئيسية من هذه المكتبة هنا.

إذا كنت تستخدم هذا الكود سوف يكون مثل هذا الطقس:

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

هذه المكتبة أيضا جراب (انظر CocoaPods)

من دائرة الرقابة الداخلية 10 ، يمكنك استخدام NSArray 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())
    }
}

إن عناصر يكرر.

على سبيل المثالمجموعة:A A A B B أو B B A A A

الحل الوحيد هو:A B A B A

sequenceSelected هو NSMutableArray الذي يخزن عناصر من فئة الكائنات التي هي مؤشرات إلى بعض تسلسل.

- (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 عناصر هذه الدالة ترجع دائما inversed مجموعة, لأنك توليد مجموعة من عشوائية على بقية المؤشرات.أكثر دقة 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