3

I want to make limitation for random class in c# like generate random variables from 2 ranges without repeat it? example :

Xpoints[i] = random.Next(0, 25);
Ypoints[i] = random.Next(0, 12);

where 25 we 12 is image dimension so I need all pixels in this image but random ? any suggestion if I use this code i didn't get some pixels and some pixels repeated

kartal
  • 17,436
  • 34
  • 100
  • 145

6 Answers6

3

Have a look at the Fisher-Yates Algorithm:

http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

It's easy to implement, and works really well.

It shuffles an array of digits, then you can access them sequentially if you like to ensure no repeats.

Darren Young
  • 10,972
  • 36
  • 91
  • 150
3

Update Simplified by not requiring any specific hashing [1]

Update Generalzed into generic SimpleShuffle extension method

        public static IEnumerable<T> SimpleShuffle<T>(this IEnumerable<T> sequence)
        {
            var rand = new Random();
            return sequence.Select(i => new {i, k=rand.Next()})
                .OrderBy(p => p.k)
                .Select(p => p.i);
        }

I though in addition to downvoting (shouting? sorry :)) Anx's answer I thought it'd be nicer to also show what my code would look like:

using System;
using System.Linq;
using System.Collections.Generic;

namespace NS
{
    static class Program
    {
        public static IEnumerable<T> SimpleShuffle<T>(this IEnumerable<T> sequence)
        {
            var rand = new Random();
            return sequence.Select(i => new {i, k=rand.Next()}).OrderBy(p => p.k).Select(p => p.i);
        }

        public static void Main(string[] args)
        {
            var pts = from x in Enumerable.Range(0, 24)
                from y in Enumerable.Range(0, 11)
                select new { x, y };

            foreach (var pt in pts.SimpleShuffle())
                Console.WriteLine("{0},{1}", pt.x, pt.y);
        }
    }
}

I totally fixed my earlier problem of how to generate a good hash by realizing that we don't need a hash unless:

  • a. the source contains (logical) duplicates
  • b. and we need those to have equivalent sort order
  • c. and we want to have the same 'random' sort order (deterministic hashing) each time round

a. and b. are false in this case and c. was even going to be a problem (depending on what the OP was requiring). So now, without any strings attached, no more worries about performance (even the irrational worries),

Good luck!

[1] Incidentally this makes the whole thing more flexible because I no longer require the coords to be expressed a byte[]; you can now shuffle any structure you want.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I tried your answer it is more faster but are you sure there is no repeated peers ? – kartal Mar 27 '11 at 22:45
  • @salamonti: yup there cannot be repeats because we're simply sorting a finite collection by a certain key. OrderBy never affects sequence length – sehe Mar 27 '11 at 22:58
1

You might want to use a shuffle algorithm on a list of the indexes (e.g. 25 elements with the values 0..24 for the X axis) instead of random.

By design random doesn't guarantee that no value is repeated; repetitions are very likely.

See also: Optimal LINQ query to get a random sub collection - Shuffle (look for the Fisher-Yates-Durstenfeld solution)

Community
  • 1
  • 1
Lucero
  • 59,176
  • 9
  • 122
  • 152
  • +1 for linking a proper implementation. I like what Jon Skeet said about early-yielding sort algortihms as well. Shuffle is effectively a sort by a random (crypto) hash... So you could also order by a crypthash and then use ordinary sort – sehe Mar 27 '11 at 22:09
1

I also believe, Random should not be predictable, and we shouldn't even predict that the value will not be repeating.

But I think sometimes it could be required to randomly get non repeating int, for that we need to maintain state, like for particular instance of Random class, what all values were returned.

here is a small quick and dirty implementation of an algorithm which I thought just now, I am not sure if it is the same as Fisher-Yates solution. I just wrote this class so that you can use it instead of System.Random class.

So It may help you for your requirement, use below NonRepeatingRandom class as per your need...

class NonRepeatingRandom : Random
{
    private HashSet<int> _usedValues = new HashSet<int>();
    public NonRepeatingRandom()
    {

    }
    public NonRepeatingRandom(int seed):base(seed)
    {

    }
    public override int Next(int minValue, int maxValue)
    {
        int rndVal = base.Next(minValue, maxValue);
        if (_usedValues.Contains(rndVal))
        {
            int oldRndVal = rndVal;
            do
            {
                rndVal++;
            } while (_usedValues.Contains(rndVal) && rndVal <= maxValue);
            if (rndVal == maxValue + 1)
            {
                rndVal = oldRndVal;
                do
                {
                    rndVal--;
                } while (_usedValues.Contains(rndVal) && rndVal >= minValue);
                if (rndVal == minValue - 1)
                {
                    throw new ApplicationException("Cannot get non repeating random for provided range.");
                }
            }
        }
        _usedValues.Add(rndVal);
        return rndVal;
    }
}

Please not that only "Next" method is overridden, and not other, if you want you can override other methods of "Random" class too.

Ps. Just before clicking "Post Your Answer" I saw sehe's answer, I liked his overall idea, but to hash 2 bytes, creating a 16 byte hash? or am I missing something? In my code I am using HashSet which uses int's implementation of GetHashCode method, which is nothing but that value of int itself so no overhead of hashing. But I could be missing some point as it is 3:59 AM here in India :)

Hope it helps salamonti...

Vishalgiri
  • 484
  • 1
  • 4
  • 15
  • Yeah: like I said, you could come with a simple hash like `(pt[1]*random1)^(pt[0]*random2)` with random1 and random2 sufficiently large static random integers. I might edit the code to include a real hash later (but there are pitfalls: need to think of using `unchecked` multiplcations etc. NEVER do hash implementations after midnight is SERIOUS :) – sehe Mar 27 '11 at 23:05
  • Oh, and by the way this answer is _not_ Fisher-Yates by any stretch of imagination. It's just Anx's solution but with the lookup extracted. And yes, a Hashset might use 'hashcodes' internally but not for sorting. TRWTF with your approach is that you must keep a growing list of ints in memory. This doesn't scale. (Also, Int32.GetHashCode() return the Int32 value by definition... sic; I'm using a hash for entropy, you are using a hash for 'quick lookup' only) – sehe Mar 27 '11 at 23:08
  • Thanks sehe, for clarifying, I thought salamonti needs an additional functionality in Random class, so I just extended the random class for non repetitive-ness, I did not read that he needs all the points but in a random order. – Vishalgiri Mar 27 '11 at 23:26
  • However, about size memory, as per your code it will use 528 bytes, for var pts, and for my code it depends upon how many times you call Next Method for a particular instance, and till the scope of the instance, and may be it will be the same. – Vishalgiri Mar 27 '11 at 23:29
  • About the "Random" things, if it is about image the sequence of points in your method will be always the same, because you are actually Sorting them, using orderby, so orderby operation for a huge image (or points) will be more expensive, and if someone needs those points randomly, sorting/orderby is not an option at all. unless you use hash + cryptographic nonce (again a random). – Vishalgiri Mar 27 '11 at 23:34
  • @Vishalgiri: you may believe those performance characteristics to be correct. It will depend on framework implementation. Head over to [Jon Skeet's "Reimplementing LINQ to Objects](http://msmvps.com/blogs/jon_skeet/archive/2011/02/23/reimplementing-linq-to-objects-part-45-conclusion-and-list-of-posts.aspx?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+JonSkeetCodingBlog+%28Jon+Skeet%27s+Coding+Blog%29) to get an ieda of _just how much_ thought goes into that. Until that time, I wish you well with your benchmarks, and remember: premature optimization is bad, clean code is not – sehe Mar 28 '11 at 06:30
  • @sehe, the new updated version is good and it should provide points in random order, in your old code was just giving points which was order by md5 has, so that order would be same everytime, now as you used random, it should work nice and smooth, You are very good at LINQ... – Vishalgiri Mar 28 '11 at 07:44
0

The whole point of random numbers is that you do get repeats.

However, if you want to make sure you don't then remove the last chosen value from your array before picking the next number. So if you have a list of numbers:

index = random.Next(0, originallist.Length);
radomisedList.Add(originalList[index]);
originalList.RemoveAt(index);

The list will be randomised yet contain no repeats.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
0

Instead of creating image through two one-dimensional arrays you should create an image through one two-dimensional matrix. Each time you get new random coordinate check if that pixel is already set. If it is then repeat the procedure for that pixel.

Dialecticus
  • 16,400
  • 7
  • 43
  • 103
  • The last few pixels may be very "hard to reach" when using random coordinates, since both the X and Y coordinates have to match the remaining pixels. Therefore this doesn't seem like an efficient solution. – Lucero Mar 27 '11 at 21:31
  • That depends on percentage of needed pixels. 25×12=300, 50% is easily reachable, so 150 pixels will cause no problems. – Dialecticus Mar 27 '11 at 21:34
  • Quote from the question: [...] so I need all pixels [...] – Lucero Mar 27 '11 at 21:39
  • unfortunately i need 300 pixel – kartal Mar 27 '11 at 21:39
  • I ignored that quoted part because it does not makes sense. If all pixels should be set then why not just set them with a loop? – Dialecticus Mar 27 '11 at 21:45