เลือกองค์ประกอบสุ่มจากชุด


180

ฉันจะเลือกองค์ประกอบแบบสุ่มจากชุดได้อย่างไร ฉันสนใจเป็นพิเศษในการเลือกองค์ประกอบแบบสุ่มจาก HashSet หรือ LinkedHashSet ใน Java โซลูชั่นสำหรับภาษาอื่น ๆ ก็ยินดีต้อนรับ


5
คุณควรระบุเงื่อนไขบางอย่างเพื่อดูว่านี่เป็นสิ่งที่คุณต้องการจริงๆหรือไม่ - คุณจะเลือกองค์ประกอบแบบสุ่มกี่ครั้ง? - ข้อมูลจำเป็นต้องเก็บไว้ใน HashSet หรือ LinkedHashSet หรือไม่และไม่สามารถเข้าถึงได้แบบสุ่ม - แฮชมีขนาดใหญ่หรือไม่ กุญแจมีขนาดเล็ก?
David Nehme

คำตอบ:


88
int size = myHashSet.size();
int item = new Random().nextInt(size); // In real life, the Random object should be rather more shared than this
int i = 0;
for(Object obj : myhashSet)
{
    if (i == item)
        return obj;
    i++;
}

94
ถ้า myHashSet มีขนาดใหญ่สิ่งนี้จะเป็นวิธีแก้ปัญหาที่ค่อนข้างช้าตั้งแต่โดยเฉลี่ยจะต้องทำซ้ำ (n / 2) เพื่อค้นหาวัตถุแบบสุ่ม
daniel

6
หากข้อมูลของคุณอยู่ในชุดแฮชคุณต้องใช้เวลา O (n) ไม่มีวิธีแก้ไขหากคุณเพียงแค่เลือกองค์ประกอบเดียวและข้อมูลถูกเก็บไว้ใน HashSet
David Nehme

8
@David Nehme: นี่เป็นข้อเสียเปรียบในสเปคของ HashSet ใน Java ใน C ++ เป็นเรื่องปกติที่จะสามารถเข้าถึงถังที่ทำขึ้น hashset โดยตรงซึ่งช่วยให้เราสามารถเลือกองค์ประกอบแบบสุ่มได้อย่างมีประสิทธิภาพยิ่งขึ้น หากจำเป็นต้องใช้องค์ประกอบแบบสุ่มใน Java อาจคุ้มค่าที่จะกำหนดชุดแฮชแบบกำหนดเองที่อนุญาตให้ผู้ใช้ดูภายใต้ประทุน ดู [doc's boost] [1] สำหรับข้อมูลเพิ่มเติมในเรื่องนี้ [1] boost.org/doc/libs/1_43_0/doc/html/unordered/buckets.html
Aaron McDaid

11
หากชุดไม่ได้กลายพันธุ์ผ่านการเข้าถึงหลายครั้งคุณสามารถคัดลอกไปยังอาร์เรย์แล้วเข้าถึง O (1) เพียงใช้ myHashSet.toArray ()
ykaganovich

2
@ykaganovich จะไม่ทำให้เรื่องแย่ลงเนื่องจากชุดจะต้องคัดลอกไปยังอาร์เรย์ใหม่ docs.oracle.com/javase/7/docs/api/java/util/ ...... "วิธีนี้จะต้องจัดสรรอาเรย์ใหม่แม้ว่าคอลเลกชันนี้จะได้รับการสนับสนุนโดยอาเรย์"
anton1980

73

คุณรู้บ้างไหม:

มีวิธีการที่เป็นประโยชน์ในการมีjava.util.Collectionsสำหรับสับคอลเลกชันทั้งหมด: และCollections.shuffle(List<?>)Collections.shuffle(List<?> list, Random rnd)


! น่ากลัว นี่ไม่ใช่การอ้างอิงข้ามที่ใดก็ได้ใน java doc! เช่นเดียวกับPython random.shuffle ()
smci

25
แต่ใช้งานได้เฉพาะกับรายการเช่นโครงสร้างที่มีฟังก์ชัน. get ()
bourbaki4481472

4
@ bourbaki4481472 ถูกต้องอย่างแน่นอน ใช้งานได้เฉพาะกับคอลเลกชันที่ขยายListอินเทอร์เฟซไม่ใช่Setอินเตอร์เฟสที่กล่าวถึงโดย OP
โทมัส

31

ทางออกที่รวดเร็วสำหรับ Java โดยใช้ArrayListและHashMap: [องค์ประกอบ -> ดัชนี]

แรงจูงใจ: ฉันต้องการชุดของรายการที่มีRandomAccessคุณสมบัติโดยเฉพาะอย่างยิ่งเพื่อเลือกรายการแบบสุ่มจากชุด (ดูpollRandomวิธีการ) การนำทางแบบสุ่มในต้นไม้ไบนารีนั้นไม่แม่นยำ: ต้นไม้ไม่สมดุลอย่างสมบูรณ์ซึ่งจะไม่นำไปสู่การแจกแจงแบบสม่ำเสมอ

public class RandomSet<E> extends AbstractSet<E> {

    List<E> dta = new ArrayList<E>();
    Map<E, Integer> idx = new HashMap<E, Integer>();

    public RandomSet() {
    }

    public RandomSet(Collection<E> items) {
        for (E item : items) {
            idx.put(item, dta.size());
            dta.add(item);
        }
    }

    @Override
    public boolean add(E item) {
        if (idx.containsKey(item)) {
            return false;
        }
        idx.put(item, dta.size());
        dta.add(item);
        return true;
    }

    /**
     * Override element at position <code>id</code> with last element.
     * @param id
     */
    public E removeAt(int id) {
        if (id >= dta.size()) {
            return null;
        }
        E res = dta.get(id);
        idx.remove(res);
        E last = dta.remove(dta.size() - 1);
        // skip filling the hole if last is removed
        if (id < dta.size()) {
            idx.put(last, id);
            dta.set(id, last);
        }
        return res;
    }

    @Override
    public boolean remove(Object item) {
        @SuppressWarnings(value = "element-type-mismatch")
        Integer id = idx.get(item);
        if (id == null) {
            return false;
        }
        removeAt(id);
        return true;
    }

    public E get(int i) {
        return dta.get(i);
    }

    public E pollRandom(Random rnd) {
        if (dta.isEmpty()) {
            return null;
        }
        int id = rnd.nextInt(dta.size());
        return removeAt(id);
    }

    @Override
    public int size() {
        return dta.size();
    }

    @Override
    public Iterator<E> iterator() {
        return dta.iterator();
    }
}

ดีที่จะทำงาน แต่คำถามคือเกี่ยวกับการตั้งค่าอินเตอร์เฟซ โซลูชันนี้บังคับให้ผู้ใช้มีการอ้างอิงชนิดที่เป็นรูปธรรมของ RandomSet
Johan Tidén

ฉันชอบวิธีนี้จริง ๆ แต่มันไม่ปลอดภัยสำหรับเธรดความไม่ถูกต้องระหว่างแผนที่และรายการอาจเกิดขึ้นดังนั้นฉันจะเพิ่มบล็อกที่ซิงโครไนซ์บางส่วน
Kostas Chalkias

@KonstantinosChalkias คอลเลกชันในตัวก็ไม่ได้ปลอดภัยเหมือนกัน เฉพาะคนที่มีชื่อConcurrentเท่านั้นที่จะปลอดภัยจริงๆคนที่ถูกมัดไว้Collections.synchronized()นั้นมีความกึ่งปลอดภัย OP ไม่ได้พูดอะไรเกี่ยวกับการทำงานพร้อมกันดังนั้นนี่เป็นคำตอบที่ถูกต้องและดี
TWiStErRob

ตัววนซ้ำที่ส่งคืนมาที่นี่ไม่ควรลบองค์ประกอบออกdta(เช่นสามารถทำได้ผ่านIterators.unmodifiableIteratorตัวอย่างของฝรั่ง) มิฉะนั้นการใช้งานเริ่มต้นของเช่น removeAll และ keepAll ใน AbstractSet และผู้ปกครองที่ทำงานร่วมกับ iterator นั้นจะทำให้คุณยุ่งเหยิงRandomSet!
muued

ทางออกที่ดี จริง ๆ แล้วคุณสามารถใช้ทรีถ้าแต่ละโหนดมีจำนวนโหนดในทรีย่อยของมัน จากนั้นคำนวณสุ่มของจริงใน 0..1 และทำการตัดสินใจแบบ 3 ทางแบบถ่วงน้ำหนัก (เลือกโหนดปัจจุบันหรือลงไปในทรีย่อยทางซ้ายหรือขวา) ที่แต่ละโหนดขึ้นอยู่กับจำนวนโหนด แต่ imo ทางออกของคุณดีกว่ามาก
ยีน

30

นี่เร็วกว่าลูปสำหรับแต่ละลูปในคำตอบที่ยอมรับ:

int index = rand.nextInt(set.size());
Iterator<Object> iter = set.iterator();
for (int i = 0; i < index; i++) {
    iter.next();
}
return iter.next();

การสร้างสำหรับแต่ละสายIterator.hasNext()บนทุกวง แต่ตั้งแต่index < set.size()ตรวจสอบนั้นไม่จำเป็นต้องมีค่าใช้จ่าย ฉันเห็นความเร็วเพิ่มขึ้น 10-20% แต่ YMMV (นอกจากนี้ยังรวบรวมโดยไม่ต้องเพิ่มคำสั่งคืนพิเศษ)

โปรดทราบว่ารหัสนี้ (และคำตอบอื่น ๆ ส่วนใหญ่) สามารถนำไปใช้กับคอลเลกชันใด ๆ ไม่เพียง แต่ตั้งค่า ในรูปแบบวิธีการทั่วไป:

public static <E> E choice(Collection<? extends E> coll, Random rand) {
    if (coll.size() == 0) {
        return null; // or throw IAE, if you prefer
    }

    int index = rand.nextInt(coll.size());
    if (coll instanceof List) { // optimization
        return ((List<? extends E>) coll).get(index);
    } else {
        Iterator<? extends E> iter = coll.iterator();
        for (int i = 0; i < index; i++) {
            iter.next();
        }
        return iter.next();
    }
}

15

หากคุณต้องการที่จะทำใน Java คุณควรพิจารณาคัดลอกองค์ประกอบลงในคอลเลกชันสุ่มเข้าถึงบางชนิด (เช่น ArrayList) เพราะนอกจากชุดของคุณจะเล็กการเข้าถึงองค์ประกอบที่เลือกจะมีราคาแพง (O (n) แทนที่จะเป็น O (1)) [ed: การคัดลอกรายการยังเป็น O (n)]

หรือคุณอาจค้นหาการติดตั้งชุดอื่นที่ตรงกับความต้องการของคุณมากขึ้น ListOrderedSetจากคอมมอนส์คอลเลกชันดูแนวโน้ม


8
การคัดลอกไปยังรายการจะเสียเวลา O (n) และใช้หน่วยความจำ O (n) ด้วยเหตุใดจึงเป็นทางเลือกที่ดีกว่าการดึงข้อมูลจากแผนที่โดยตรง
mdma

12
ขึ้นอยู่กับจำนวนครั้งที่คุณต้องการเลือกจากชุด การคัดลอกเป็นการดำเนินการครั้งเดียวจากนั้นคุณสามารถเลือกจากชุดได้บ่อยเท่าที่คุณต้องการ หากคุณเลือกเพียงองค์ประกอบเดียวก็ใช่การคัดลอกจะไม่ทำให้เร็วขึ้น
Dan Dyer

เป็นการดำเนินการเพียงครั้งเดียวหากคุณต้องการเลือกซ้ำ หากคุณต้องการลบรายการที่เลือกออกจากชุดคุณจะกลับไปที่ O (n)
TurnipEntropy


9

ใน Java:

Set<Integer> set = new LinkedHashSet<Integer>(3);
set.add(1);
set.add(2);
set.add(3);

Random rand = new Random(System.currentTimeMillis());
int[] setArray = (int[]) set.toArray();
for (int i = 0; i < 10; ++i) {
    System.out.println(setArray[rand.nextInt(set.size())]);
}

11
คำตอบของคุณใช้งานได้ แต่มันก็ไม่ได้มีประสิทธิภาพมากนักเนื่องจากส่วน set.toArray ()
เบาะแสน้อยลง

12
คุณควรย้าย toArray ไปนอกวง
David Nehme

8
List asList = new ArrayList(mySet);
Collections.shuffle(asList);
return asList.get(0);

21
นี่เป็นสิ่งที่ไม่มีประสิทธิภาพอย่างแท้จริง ตัวสร้าง ArrayList ของคุณเรียกใช้. toArray () ในชุดที่ให้มา ToArray (โดยส่วนใหญ่หากไม่ใช่การปรับใช้คอลเลกชันมาตรฐานทั้งหมด) จะวนซ้ำทั่วคอลเลกชันทั้งหมดโดยกรอกอาร์เรย์ตามที่จะไป จากนั้นคุณสลับรายการซึ่งสลับแต่ละองค์ประกอบด้วยองค์ประกอบแบบสุ่ม คุณควรจะดีกว่านี้มากเพียงแค่วนซ้ำการตั้งค่าเป็นองค์ประกอบแบบสุ่ม
Chris Bode

4

นี้เป็นเหมือนการยอมรับคำตอบ (Khoth) แต่ด้วยความที่ไม่จำเป็นsizeและiตัวแปรลบออก

    int random = new Random().nextInt(myhashSet.size());
    for(Object obj : myhashSet) {
        if (random-- == 0) {
            return obj;
        }
    }

แม้ว่าการทำไปกับตัวแปรทั้งสองข้างต้นวิธีการแก้ปัญหาข้างต้นยังคงสุ่มเพราะเราอาศัยการสุ่ม (เริ่มต้นที่ดัชนีสุ่มเลือก) เพื่อลดตัวเองไป0ในแต่ละรอบซ้ำ


1
บรรทัดที่สามยังอาจเป็นif (--random < 0) {ที่ต้นน้ำrandom -1
ซัลวาดอ

3

สารละลาย Clojure:

(defn pick-random [set] (let [sq (seq set)] (nth sq (rand-int (count sq)))))

1
วิธีการแก้ปัญหานี้ยังเป็นเชิงเส้นเพราะจะได้รับnthองค์ประกอบที่คุณต้องสำรวจseqเช่นกัน
Bruno Kim

1
มันเป็นเส้นตรงตามที่มันลงตัวในบรรทัดเดียว: D
Krzysztof Wolny

2

Perl 5

@hash_keys = (keys %hash);
$rand = int(rand(@hash_keys));
print $hash{$hash_keys[$rand]};

นี่คือวิธีหนึ่งในการทำ


2

C ++ สิ่งนี้ควรจะเร็วพอสมควรเนื่องจากไม่ต้องวนซ้ำทั้งชุดหรือจัดเรียง นี้จะทำงานออกจากกล่องกับคอมไพเลอร์ที่ทันสมัยที่สุดสมมติว่าพวกเขาสนับสนุนtr1 ถ้าไม่คุณอาจต้องใช้ Boost

เอกสาร Boostจะเป็นประโยชน์ที่นี่เพื่ออธิบายเรื่องนี้แม้ว่าคุณจะไม่ได้ใช้ Boost

เคล็ดลับคือการใช้ประโยชน์จากความจริงที่ว่าข้อมูลถูกแบ่งออกเป็นที่เก็บข้อมูลและเพื่อระบุกลุ่มที่เลือกแบบสุ่ม (ด้วยความน่าจะเป็นที่เหมาะสม) อย่างรวดเร็ว

//#include <boost/unordered_set.hpp>  
//using namespace boost;
#include <tr1/unordered_set>
using namespace std::tr1;
#include <iostream>
#include <stdlib.h>
#include <assert.h>
using namespace std;

int main() {
  unordered_set<int> u;
  u.max_load_factor(40);
  for (int i=0; i<40; i++) {
    u.insert(i);
    cout << ' ' << i;
  }
  cout << endl;
  cout << "Number of buckets: " << u.bucket_count() << endl;

  for(size_t b=0; b<u.bucket_count(); b++)
    cout << "Bucket " << b << " has " << u.bucket_size(b) << " elements. " << endl;

  for(size_t i=0; i<20; i++) {
    size_t x = rand() % u.size();
    cout << "we'll quickly get the " << x << "th item in the unordered set. ";
    size_t b;
    for(b=0; b<u.bucket_count(); b++) {
      if(x < u.bucket_size(b)) {
        break;
      } else
        x -= u.bucket_size(b);
    }
    cout << "it'll be in the " << b << "th bucket at offset " << x << ". ";
    unordered_set<int>::const_local_iterator l = u.begin(b);
    while(x>0) {
      l++;
      assert(l!=u.end(b));
      x--;
    }
    cout << "random item is " << *l << ". ";
    cout << endl;
  }
}

2

โซลูชันข้างต้นพูดในรูปแบบของเวลาแฝง แต่ไม่รับประกันความน่าจะเป็นที่เท่ากันของแต่ละดัชนีที่ถูกเลือก
หากจำเป็นต้องพิจารณาให้ลองสุ่มตัวอย่างอ่างเก็บน้ำ http://en.wikipedia.org/wiki/Reservoir_sampling
Collections.shuffle () (ตามที่แนะนำโดยบางคน) ใช้หนึ่งอัลกอริทึมดังกล่าว


1

เนื่องจากคุณกล่าวว่า "ยินดีต้อนรับโซลูชันภาษาอื่น ๆ ด้วย" นี่เป็นเวอร์ชั่นสำหรับ Python:

>>> import random
>>> random.choice([1,2,3,4,5,6])
3
>>> random.choice([1,2,3,4,5,6])
4

3
เฉพาะ [1,2,3,4,5,6] ไม่ใช่ชุด แต่เป็นรายการเนื่องจากไม่รองรับสิ่งต่าง ๆ เช่นการค้นหาที่รวดเร็ว
โทมัส Ahle

คุณยังสามารถทำได้: >>> random.choice (รายการ (ชุด (ช่วง (5)))) >>> 4 ไม่เหมาะ แต่มันจะทำถ้าคุณต้องการ
SapphireSun

1

คุณไม่สามารถรับขนาด / ความยาวของชุด / อาร์เรย์สร้างตัวเลขสุ่มระหว่าง 0 ถึงขนาด / ความยาวแล้วเรียกองค์ประกอบที่ดัชนีตรงกับตัวเลขนั้นหรือไม่ HashSet มีวิธีการขนาด. () ฉันค่อนข้างแน่ใจ

ใน psuedocode -

function randFromSet(target){
 var targetLength:uint = target.length()
 var randomIndex:uint = random(0,targetLength);
 return target[randomIndex];
}

ใช้งานได้เฉพาะถ้าคอนเทนเนอร์ที่มีปัญหารองรับการค้นหาดัชนีแบบสุ่ม การใช้งานคอนเทนเนอร์จำนวนมากไม่ (เช่นตารางแฮชต้นไม้ไบนารีรายการที่เชื่อมโยง)
David Haley

1

PHP โดยสมมติว่า "set" เป็นอาร์เรย์:

$foo = array("alpha", "bravo", "charlie");
$index = array_rand($foo);
$val = $foo[$index];

ฟังก์ชั่น Mersenne Twister ดีกว่า แต่ไม่มีค่าเทียบเท่ากับ array_rand ของ MT ใน PHP


การใช้งานชุดส่วนใหญ่ไม่มีตัวรับ get (i) หรือตัวทำดัชนีดังนั้น id สมมติว่าทำไม OP ระบุชุดของมัน
DownloadPizza

1

ไอคอนมีประเภทชุดและตัวดำเนินการองค์ประกอบแบบสุ่ม unary "?" ดังนั้นการแสดงออก

? set( [1, 2, 3, 4, 5] )

จะสร้างตัวเลขสุ่มระหว่าง 1 ถึง 5

การสุ่มเริ่มต้นเป็น 0 เมื่อมีการเรียกใช้โปรแกรมดังนั้นเพื่อให้ได้ผลลัพธ์ที่แตกต่างกันในการใช้งานแต่ละครั้ง randomize()


1

ใน C #

        Random random = new Random((int)DateTime.Now.Ticks);

        OrderedDictionary od = new OrderedDictionary();

        od.Add("abc", 1);
        od.Add("def", 2);
        od.Add("ghi", 3);
        od.Add("jkl", 4);


        int randomIndex = random.Next(od.Count);

        Console.WriteLine(od[randomIndex]);

        // Can access via index or key value:
        Console.WriteLine(od[1]);
        Console.WriteLine(od["def"]);

ดูเหมือนว่าพวกเขา downvoted เพราะพจนานุกรม java เส็งเคร็ง (หรือที่เรียกว่า LinkedHashSet ไม่ว่าอะไรก็ตามที่เป็น) ไม่สามารถ "เข้าถึงแบบสุ่ม" (ซึ่งถูกเข้าถึงโดยกุญแจฉันเดา) อึจาวาทำให้ฉันหัวเราะมาก
Federico Berasategui

1

โซลูชัน Javascript;)

function choose (set) {
    return set[Math.floor(Math.random() * set.length)];
}

var set  = [1, 2, 3, 4], rand = choose (set);

หรืออีกทางหนึ่ง:

Array.prototype.choose = function () {
    return this[Math.floor(Math.random() * this.length)];
};

[1, 2, 3, 4].choose();

ฉันชอบทางเลือกที่สอง :-)
marcospereira

โอ้ฉันชอบการเพิ่มวิธีอาร์เรย์ใหม่!
matt lohkamp

1

ในเสียงกระเพื่อม

(defun pick-random (set)
       (nth (random (length set)) set))

ใช้งานได้กับรายการใช่ไหม ด้วยELTมันสามารถทำงานได้สำหรับลำดับใด ๆ
Ken

1

ใน Mathematica:

a = {1, 2, 3, 4, 5}

a[[  Length[a] Random[]  ]]

หรือในเวอร์ชันล่าสุดเพียง:

RandomChoice[a]

สิ่งนี้ได้รับการลงคะแนนเสียงบางทีอาจเป็นเพราะมันไม่มีคำอธิบายดังนั้นนี่คือ:

Random[]สร้างทุ่นลอยเทียมสุ่มระหว่าง 0 ถึง 1 ซึ่งจะถูกคูณด้วยความยาวของรายการจากนั้นฟังก์ชั่นเพดานจะถูกใช้เพื่อปัดเศษให้เป็นจำนวนเต็มถัดไป ดัชนีนี้จะถูกแยกออกมาจากaดัชนีนี้เป็นสารสกัดจากนั้น

เนื่องจากฟังก์ชันตารางแฮชมักจะทำกับกฎใน Mathematica และกฎจะถูกเก็บไว้ในรายการจึงอาจใช้:

a = {"Badger" -> 5, "Bird" -> 1, "Fox" -> 3, "Frog" -> 2, "Wolf" -> 4};


1

เพื่อความสนุกสนานฉันเขียน RandomHashSet โดยใช้การสุ่มตัวอย่างการปฏิเสธ มันค่อนข้างแฮ็คเนื่องจาก HashMap ไม่อนุญาตให้เราเข้าถึงตารางโดยตรง แต่ควรใช้งานได้ดี

ไม่ใช้หน่วยความจำเพิ่มเติมและเวลาค้นหาคือ O (1) ตัดจำหน่าย (เนื่องจาก java HashTable มีความหนาแน่นสูง)

class RandomHashSet<V> extends AbstractSet<V> {
    private Map<Object,V> map = new HashMap<>();
    public boolean add(V v) {
        return map.put(new WrapKey<V>(v),v) == null;
    }
    @Override
    public Iterator<V> iterator() {
        return new Iterator<V>() {
            RandKey key = new RandKey();
            @Override public boolean hasNext() {
                return true;
            }
            @Override public V next() {
                while (true) {
                    key.next();
                    V v = map.get(key);
                    if (v != null)
                        return v;
                }
            }
            @Override public void remove() {
                throw new NotImplementedException();
            }
        };
    }
    @Override
    public int size() {
        return map.size();
    }
    static class WrapKey<V> {
        private V v;
        WrapKey(V v) {
            this.v = v;
        }
        @Override public int hashCode() {
            return v.hashCode();
        }
        @Override public boolean equals(Object o) {
            if (o instanceof RandKey)
                return true;
            return v.equals(o);
        }
    }
    static class RandKey {
        private Random rand = new Random();
        int key = rand.nextInt();
        public void next() {
            key = rand.nextInt();
        }
        @Override public int hashCode() {
            return key;
        }
        @Override public boolean equals(Object o) {
            return true;
        }
    }
}

1
สิ่งที่ฉันคิด! คำตอบที่ดีที่สุด!
mmm

ที่จริงแล้วการกลับมาฉันคิดว่ามันไม่เหมือนกันถ้า hashmap มีการชนกันหลายครั้งและเราทำการค้นหาหลายครั้ง นั่นเป็นเพราะ java hashmap ใช้ที่เก็บข้อมูล / การเชื่อมโยงและโค้ดนี้จะส่งคืนองค์ประกอบแรกในที่ฝากข้อมูลเฉพาะเสมอ เรายังคงเหมือนกันกับการสุ่มของฟังก์ชันแฮช
โทมัส Ahle

1

Java 8 ที่ง่ายที่สุดคือ:

outbound.stream().skip(n % outbound.size()).findFirst().get()

โดยที่nเป็นจำนวนเต็มแบบสุ่ม แน่นอนว่ามันมีประสิทธิภาพที่ต่ำกว่าด้วยfor(elem: Col)


1

ด้วยGuavaเราสามารถทำได้ดีกว่าคำตอบของ Khoth เล็กน้อย:

public static E random(Set<E> set) {
  int index = random.nextInt(set.size();
  if (set instanceof ImmutableSet) {
    // ImmutableSet.asList() is O(1), as is .get() on the returned list
    return set.asList().get(index);
  }
  return Iterables.get(set, index);
}

0

PHP โดยใช้ MT:

$items_array = array("alpha", "bravo", "charlie");
$last_pos = count($items_array) - 1;
$random_pos = mt_rand(0, $last_pos);
$random_item = $items_array[$random_pos];

0

คุณยังสามารถถ่ายโอนชุดไปยังอาร์เรย์ใช้อาร์เรย์มันอาจจะทำงานในขนาดเล็กฉันเห็นการวนรอบในคำตอบโหวตมากที่สุดคือ O (n) ต่อไป

Object[] arr = set.toArray();

int v = (int) arr[rnd.nextInt(arr.length)];

0

หากคุณเพียงแค่ต้องการเลือกวัตถุ "ใด ๆ " จากSet, โดยไม่มีการรับประกันใด ๆ เกี่ยวกับการสุ่ม, วิธีที่ง่ายที่สุดคือการส่งคืนโดย iterator เป็นครั้งแรก

    Set<Integer> s = ...
    Iterator<Integer> it = s.iterator();
    if(it.hasNext()){
        Integer i = it.next();
        // i is a "random" object from set
    }

1
นี่จะไม่ใช่การสุ่มเลือก ลองนึกภาพการดำเนินการเดียวกันผ่านชุดเดียวกันหลายครั้ง ฉันคิดว่าคำสั่งซื้อจะเหมือนกัน
Menezes Sousa

0

วิธีแก้ปัญหาทั่วไปที่ใช้คำตอบของ Khoth เป็นจุดเริ่มต้น

/**
 * @param set a Set in which to look for a random element
 * @param <T> generic type of the Set elements
 * @return a random element in the Set or null if the set is empty
 */
public <T> T randomElement(Set<T> set) {
    int size = set.size();
    int item = random.nextInt(size);
    int i = 0;
    for (T obj : set) {
        if (i == item) {
            return obj;
        }
        i++;
    }
    return null;
}

0

น่าเสียดายที่สิ่งนี้ไม่สามารถทำได้อย่างมีประสิทธิภาพ (ดีกว่า O (n)) ในคอนเทนเนอร์มาตรฐานของไลบรารีชุดใด ๆ

นี่เป็นเรื่องแปลกเนื่องจากมันง่ายมากที่จะเพิ่มฟังก์ชั่นการสุ่มแบบสุ่มไปยังชุดแฮชและชุดไบนารี ในชุดแฮชที่ไม่แพร่กระจายคุณสามารถลองสุ่มรายการจนกว่าคุณจะได้รับผลกระทบ สำหรับต้นไม้ไบนารีคุณสามารถเลือกสุ่มระหว่างทรีย่อยทางซ้ายหรือขวาพร้อมสูงสุด O (log2) ขั้นตอน ฉันได้ทดลองใช้ตัวอย่างด้านล่าง:

import random

class Node:
    def __init__(self, object):
        self.object = object
        self.value = hash(object)
        self.size = 1
        self.a = self.b = None

class RandomSet:
    def __init__(self):
        self.top = None

    def add(self, object):
        """ Add any hashable object to the set.
            Notice: In this simple implementation you shouldn't add two
                    identical items. """
        new = Node(object)
        if not self.top: self.top = new
        else: self._recursiveAdd(self.top, new)
    def _recursiveAdd(self, top, new):
        top.size += 1
        if new.value < top.value:
            if not top.a: top.a = new
            else: self._recursiveAdd(top.a, new)
        else:
            if not top.b: top.b = new
            else: self._recursiveAdd(top.b, new)

    def pickRandom(self):
        """ Pick a random item in O(log2) time.
            Does a maximum of O(log2) calls to random as well. """
        return self._recursivePickRandom(self.top)
    def _recursivePickRandom(self, top):
        r = random.randrange(top.size)
        if r == 0: return top.object
        elif top.a and r <= top.a.size: return self._recursivePickRandom(top.a)
        return self._recursivePickRandom(top.b)

if __name__ == '__main__':
    s = RandomSet()
    for i in [5,3,7,1,4,6,9,2,8,0]:
        s.add(i)

    dists = [0]*10
    for i in xrange(10000):
        dists[s.pickRandom()] += 1
    print dists

ฉันได้รับ [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] เป็นเอาต์พุตดังนั้นการกระจายจึงดี

ฉันได้ต่อสู้กับปัญหาเดียวกันสำหรับตัวเองและฉันยังไม่ได้ตัดสินใจว่าสภาพอากาศการเพิ่มประสิทธิภาพของตัวเลือกที่มีประสิทธิภาพมากขึ้นนั้นคุ้มค่ากับค่าใช้จ่ายในการใช้คอลเล็กชั่นจากไพ ธ อน แน่นอนว่าฉันสามารถปรับแต่งและแปลเป็น C แต่มันทำงานได้ดีเกินไปสำหรับฉันในวันนี้ :)


1
เหตุผลที่ฉันคิดว่าสิ่งนี้ไม่ได้นำมาใช้ในต้นไม้ไบนารีเพราะวิธีการดังกล่าวจะไม่เลือกรายการที่เหมือนกัน เนื่องจากโหนดเหล่านั้นเป็นโหนดที่ไม่มีลูกซ้าย / ขวาสถานการณ์อาจเกิดขึ้นเมื่อชายด์ซ้ายมีไอเท็มมากกว่าชายด์ที่ถูกต้อง (หรือกลับกัน) สิ่งนี้จะทำให้การเลือกไอเท็มทางขวา (หรือซ้าย) ชายด์มีแนวโน้มมากขึ้น
Willem Van Onsem

1
@CommuSoft: นั่นเป็นเหตุผลที่ฉันเก็บขนาดของทรีย่อยแต่ละอันเพื่อให้ฉันสามารถเลือกความน่าจะเป็นของฉันได้
โทมัส Ahle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.