এনএসমিটেবলআরে বদলে যাওয়ার সর্বোত্তম উপায় কী?


187

আপনার যদি একটি থাকে তবে আপনি NSMutableArrayকীভাবে উপাদানগুলিকে এলোমেলোভাবে বদলে ফেলবেন?

(এটির জন্য আমার নিজস্ব উত্তর আছে, যা নীচে পোস্ট করা হয়েছে, তবে আমি কোকোতে নতুন এবং আরও ভাল উপায় আছে কিনা তা জানতে আগ্রহী))


আপডেট: @ মুকেশ দ্বারা উল্লিখিত হিসাবে, আইওএস 10+ এবং ম্যাকোস 10.12+ হিসাবে, এমন একটি -[NSMutableArray shuffledArray]পদ্ধতি রয়েছে যা রদবদল করতে ব্যবহার করা যেতে পারে। বিশদের জন্য https://developer.apple.com/docamentation/foundation/nsarray/1640855-shuffledarray?language=objc দেখুন । (তবে মনে রাখবেন যে এটি উপাদানগুলিকে স্থান পরিবর্তন না করে বরং নতুন অ্যারে তৈরি করে))


এই প্রশ্নটি একবার দেখুন: আপনার বদলানো অ্যালগরিদমের প্রতি শ্রদ্ধাবোধের সাথে সত্যিকারের বিশ্ব সমস্যাগুলি uff
ক্রেগব


5
বর্তমান উত্তম ফিশার-ইয়েটস :for (NSUInteger i = self.count; i > 1; i--) [self exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
Coeur

2
ছেলেরা, আইওএস 10 ++ থেকে অ্যাপল প্রদত্ত অ্যারে সম্পর্কে নতুন ধারণাটি, এই উত্তরটি দেখুন
মুকেশ

বিদ্যমান সমস্যাটি APIহ'ল এটি একটি নতুনকে ফেরত দেয় Arrayযা মেমরিতে নতুন অবস্থানে সম্বোধন করে।
দি টাইগার

উত্তর:



349

আমি এনএসমেটেবলআর্রে একটি বিভাগ যুক্ত করে এটি সমাধান করেছি।

সম্পাদনা করুন: লেড দ্বারা উত্তর দেওয়ার জন্য অপ্রয়োজনীয় পদ্ধতিটি সরানো হয়েছে।

সম্পাদনা: গ্রেগরি গোলটসভের জবাব এবং মিহো এবং ব্লাহাদিবলাহের মন্তব্যে ধন্যবাদ জানাতে পরিবর্তিত (arc4random() % nElements)হয়েছেarc4random_uniform(nElements)

সম্পাদনা করুন: লুপ উন্নতি, রনের মন্তব্যের জন্য ধন্যবাদ

সম্পাদনা করুন: যোগ করা চেকটি অ্যারে খালি নয়, মহেশ আগরওয়ালের মন্তব্যের জন্য ধন্যবাদ

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

10
সুন্দর সমাধান। এবং হ্যাঁ, উইল 2 যেমন উল্লেখ করেছে, র্যান্ডম () এর পরিবর্তে আরক 4 ব্র্যান্ডম () এর সাথে প্রতিস্থাপন করা একটি দুর্দান্ত উন্নতি কারণ কোনও বীজ বপনের প্রয়োজন নেই।
জেসন মুর

4
@ জেসন: কখনও কখনও (যেমন পরীক্ষার সময়), বীজ সরবরাহ করতে সক্ষম হওয়া ভাল জিনিস। ক্রিস্টোফার: দুর্দান্ত অ্যালগরিদম। : এটা ফিশার-ইয়েটস আলগোরিদিম একটি বাস্তবায়ন এর en.wikipedia.org/wiki/Fisher-Yates_shuffle
JeremyP

4
একটি অতি ক্ষুদ্রতর উন্নতি: লুপের শেষ পুনরাবৃত্তিতে, i == গণনা - ১. এর অর্থ এই নয় যে আমরা সূচি i তে নিজের সাথে বস্তুটি বিনিময় করছি? সর্বদা শেষ পুনরাবৃত্তিটি এড়ানোর জন্য আমরা কি কোডটি টুইট করতে পারি?
রন

10
আপনি যদি কোনও মুদ্রাকে কেবল উল্টানো হিসাবে বিবেচনা করেন তবে ফলাফলটি মূলত যে দিকটি ছিল তার বিপরীত দিকে?
ক্রিস্টোফার জনসন

4
এই পরিবর্তনগুলি সুক্ষভাবে পক্ষপাতদুষ্ট। arc4random_uniform(nElements)পরিবর্তে ব্যবহার করুন arc4random()%nElements। আরও তথ্যের জন্য arc4random ম্যান পৃষ্ঠা এবং মডুলো পক্ষপাতের এই ব্যাখ্যা দেখুন।
blahdiblah

38

যেহেতু আমি এখনও মন্তব্য করতে পারি না, তাই আমি ভেবেছিলাম আমি একটি পূর্ণ প্রতিক্রিয়া অবদান করব। আমি আমার প্রকল্পের জন্য ক্রিস্টোফার জনসনের বাস্তবায়নকে বিভিন্ন উপায়ে পরিবর্তন করেছি (সত্যই এটি যতটা সম্ভব সংক্ষিপ্ত করে তোলার চেষ্টা করছি), তাদের মধ্যে একটি হ'ল 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

2
নোট করুন যে আপনি [self count]লুপের মাধ্যমে প্রতিটি পুনরাবৃত্তিতে দুবার কল (একটি সম্পত্তি প্রাপ্তকারী) করছেন। আমি মনে করি এটি লুপের বাইরে সরিয়ে নেওয়া সংক্ষিপ্ততার ক্ষতি হিসাবে মূল্যবান।
ক্রিস্টোফার জনসন

1
এবং সে কারণেই আমি এর [object method]পরিবর্তে এখনও পছন্দ করি object.method: লোকেরা ভুলে যায় যে পরবর্তীতে কোনও স্ট্রাক্ট সদস্যের অ্যাক্সেসের মতো সস্তা নয়, এটি একটি পদ্ধতি কলের জন্য আসে ... একটি লুপে খুব খারাপ।
ডার্কডাস্ট

সংশোধন করার জন্য আপনাকে ধন্যবাদ - আমি ভুলভাবে ধরে নিয়েছি যে কোনও কারণে, গণনাটি ক্যাশে হয়েছিল। উত্তর আপডেট করেছেন।
গ্রেগলটসভ

10

আপনি যদি আমদানি GameplayKitকরেন তবে একটি shuffledএপিআই রয়েছে:

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

let shuffledArray = array.shuffled()

আমার মাইআর্রে রয়েছে এবং এটির একটি নতুন শফলআরে তৈরি করতে চাই। আমি এটি উদ্দেশ্য - সি দিয়ে কীভাবে করব?
ওমকার যাদব

shuffledArray = [array shuffledArray];
andreacipriani

নোট করুন যে এই পদ্ধতির অংশ GameplayKitতাই আপনাকে এটি আমদানি করতে হবে।
আন্থোপাক

9

একটি সামান্য উন্নত এবং সংক্ষিপ্ত সমাধান (শীর্ষ উত্তরের তুলনায়)।

অ্যালগরিদম একই এবং সাহিত্যে " ফিশার-ইয়েটস সাফল্য " হিসাবে বর্ণনা করা হয় ।

উদ্দেশ্য-সি তে:

@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])
        }
    }
}

দ্রষ্টব্য: আইওএস 10 ব্যবহার করে সুইফটে আরও সংক্ষিপ্ত সমাধান সম্ভব GameplayKit

দ্রষ্টব্য: অস্থির স্থানচ্যুতকরণের জন্য একটি অ্যালগরিদম (সমস্ত অবস্থানের সাথে>> 1 গণনা করাতে বাধ্য হয়) এছাড়াও উপলব্ধ


এই এবং ক্রিস্টোফার জনসনের অ্যালগরিদমের মধ্যে পার্থক্য কী হবে?
আইলিয়ান ওনোফ্রেই

@ ইলিয়ানঅনফ্রেই, মূলত, ক্রিস্টোফার জনসনের কোডটি অনুকূল ছিল না এবং আমি তার উত্তরটি উন্নত করেছিলাম, তারপরে এটি আবার কিছু সম্পাদনহীন প্রাথমিক চেক যুক্ত হয়ে সম্পাদিত হয়েছিল। আমি এটি লেখার আমার সংক্ষিপ্ত পদ্ধতির পছন্দ করি। অ্যালগরিদম একই এবং সাহিত্যে " ফিশার-ইয়েটস সাফল্য " হিসাবে বর্ণনা করা হয় ।
সিউর

6

এটি এনএসআরাই বা এনএস মিউটেবলআরাইস বদল করার সহজতম এবং দ্রুততম উপায় (অবজেক্ট ধাঁধাটি একটি এনএস সামিউটেবল অ্যারে রয়েছে, এতে ধাঁধা অবজেক্ট রয়েছে I've

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

আপনি পাশাপাশি প্রবৃত্তি 2 এর সাথে অজেক্ট 2 এর সাথে তুলনা করতে পারেন এবং সিদ্ধান্ত নিতে পারেন যে আপনি সম্ভাব্য মানগুলি কী ফিরিয়ে দিতে চান:

  • এনএসআরডআরসেন্ডিং = -1
  • এনএসআরআরডসেম = 0
  • এনএসঅর্ডার্ডডিজেন্ডিং = 1

1
এছাড়াও এই সমাধানের জন্য, আরক 4 ব্র্যান্ডম () বা বীজ ব্যবহার করুন।
জোহান কুল

17
এই পরিবর্তনগুলি ত্রুটিযুক্ত - যেমন মাইক্রোসফ্টকে সম্প্রতি স্মরণ করিয়ে দেওয়া হয়েছে: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
রাফেল শোয়েকার্ট

সম্মত, ত্রুটিযুক্ত কারণ এমএস সম্পর্কে সেই নিবন্ধে নির্দেশিত হিসাবে "বাছাইয়ের ক্রম আদেশের একটি স্ব-ধারাবাহিক সংজ্ঞা প্রয়োজন"। মার্জিত দেখতে, তবে তা নয়।
জেফ

2

একটি দুর্দান্ত জনপ্রিয় গ্রন্থাগার রয়েছে, এর এই অংশটি এটির অংশ হিসাবে রয়েছে, এটি গিটহাবে এসএসটুলকিট নামে পরিচিত । NSMutableArray + SSToolkitAdditions.h ফাইলটিতে শফল পদ্ধতি রয়েছে। আপনি এটি ব্যবহার করতে পারেন। এর মধ্যে অনেকগুলি দরকারী জিনিস রয়েছে বলে মনে হয়।

এই লাইব্রেরির মূল পৃষ্ঠাটি এখানে

আপনি যদি এটি ব্যবহার করেন তবে আপনার কোডটি এর মতো হবে:

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

এই লাইব্রেরিতে একটি পডও রয়েছে (কোকোপডগুলি দেখুন)


2

আইওএস 10 থেকে, আপনি গেমপ্লেকিট shuffled()থেকে এনএসআরাই ব্যবহার করতে পারেন । সুইফট 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())
    }
}

1

যদি উপাদানগুলির পুনরাবৃত্তি হয়।

যেমন অ্যারে: এএএবিবি বা বিবিএএএ

একমাত্র সমাধান হ'ল: আবাবা

sequenceSelected এটি একটি এনএসমিটেবলআরেই যা শ্রেণি অবজেক্টের উপাদানগুলি সঞ্চয় করে, যা কিছু অনুক্রমের নির্দেশক।

- (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প্রতিরোধ একাধিক দৃষ্টান্ত কাজ: এটা অনেক নিরাপদ এবং দুটি পদ্ধতি, একটি প্রধান এক যে এলোমেলো করে এবং মাধ্যমিক পদ্ধতি কল ব্যবহার করতে পাঠযোগ্য হবে, যখন মাধ্যমিক পদ্ধতিটি কেবল নিজেই কল এবং রদবদল না। এছাড়াও একটি বানান ভুল আছে।
সিউর

-1
NSUInteger randomIndex = arc4random() % [theArray count];

2
বা arc4random_uniform([theArray count])ম্যাক ওএস এক্স বা আইওএসের যে সংস্করণটিকে আপনি সমর্থন করছেন তার সংস্করণে উপলভ্য থাকলে বা আরও ভাল হতে পারে।
ক্রিস্টোফার জনসন

1
আমি এই মত নম্বর দিয়েছি পুনরাবৃত্তি হবে।
ভিনিশ টিপি

-1

ক্রিস্টোফার জনসনের উত্তরটি বেশ সুন্দর, তবে এটি সম্পূর্ণ এলোমেলো নয়।

2 টি উপাদানের একটি অ্যারে দেওয়া, এই ফাংশনটি সর্বদা বিপরীত অ্যারেটি দেয়, কারণ আপনি বাকী সূচকগুলিতে আপনার এলোমেলো পরিসীমা তৈরি করছেন are একটি আরও সঠিক 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.codinghorror.com/the-danger-of-naivete দেখুন । আমি মনে করি যে আমার উত্তরটিতে দুটি মাত্র থাকলে উপাদানগুলিকে অদলবদলের 50% সুযোগ রয়েছে: যখন আমি শূন্য হয়, arc4random_uniform (2) হয় 0 বা 1 ফেরত আসবে, সুতরাং জিরোথ উপাদানটি হয় নিজের সাথে বিনিময় হবে বা অনথের সাথে বিনিময় হবে উপাদান। পরবর্তী পুনরাবৃত্তিতে, যখন আমি 1 হবে, আরক 4 ব্র্যান্ডম (1) সর্বদা 0 ফিরে আসবে এবং আইথ উপাদানটি সর্বদা নিজের সাথে বিনিময় করা হবে যা অকার্যকর তবে ভুল নয়। (সম্ভবত লুপের অবস্থা হওয়া উচিত i < (count-1)))
ক্রিস্টোফার জনসন

-2

সম্পাদনা: এটি সঠিক নয়। রেফারেন্সের উদ্দেশ্যে, আমি এই পোস্টটি মুছলাম না। এই পদ্ধতির সঠিক না হওয়ার কারণ সম্পর্কে মন্তব্যগুলি দেখুন।

এখানে সাধারণ কোড:

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.