1

I`m facing with the problem of putting char in random position.

I have a table full of dots and I have to replace 30% of these dots with *

Size: 10x5

I used function Random.

Random rnd = new Random();

if (rnd.Next() % 10 > 3)
    Console.Write(". ");
else
    Console.Write("* ");

Everything is in 2 loops which hold Length and Height of table (10x5).

But it only makes PROBABILITY of 30% to make * instead of .

It takes good position but every time I start a program there is different amount of *.

It should just have 16 of * (17 - if rounded) every time I start the program

How should I suppose to make 30% always instead of probability?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Martin
  • 49
  • 1
  • 7
  • 2
    So you've 50 stars in a table? And, you want to make almost 14 `*` ? – J.SMTBCJ15 Mar 20 '17 at 11:27
  • 2
    Don’t re-initialise the random number generator inside the loop. Doing so is an (undiagnosed) error that will destroy the generator’s statistical properties. Instead of random numbers you’ll get non-random (= potentially extremely regular) garbage. – Konrad Rudolph Mar 20 '17 at 11:47

5 Answers5

5

You have 50 dots. calculate 50*30/100, it becomes 15.

Then generate 15 unique random numbers within range of 0 to 50. those numbers are indexes you have to replace . with *

var indexes = Enumerable.Range(0,50).OrderBy(x => rng.Next()).Take(50*30/100).ToList();

If you are working with 2d index, its fairly easy to convert 1d index into 2d index.

var i = index % 5;
var j = index / 5;

According to what @KonradRudolph said if you don't want to use OrderBy you can check out other ways to shuffle array (or create randomized set) posted here Best way to randomize an array with .NET

Here is more efficient way using Fisher-Yates algorithm that I suggest you to use instead of using OrderBy

var indexes = Enumerable.Range(0, 50).ToArray();
RandomExtensions.Shuffle(rng, indexes);
Community
  • 1
  • 1
M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • I think you'd want a `ToList()` or similar on the end of what you're assigning to `indexes`, unless you want the random selection to change each time `indexes` is referenced later. – Damien_The_Unbeliever Mar 20 '17 at 11:36
  • @Damien_The_Unbeliever very true. didn't notice deferred execution – M.kazem Akhgary Mar 20 '17 at 11:37
  • 1
    Don’t use the `OrderBy(x => rng.Next())` trick, it’s invalid code! It violates the function’s preconditions (`OrderBy` requires a valid partial ordering) and may break at any time if the implementation changes. – Konrad Rudolph Mar 20 '17 at 11:45
  • @KonradRudolph but this approach is very common, I don't think they change implementation, that would be a breaking change. – M.kazem Akhgary Mar 20 '17 at 11:47
  • 1
    @M.kazemAkhgary Just because the approach is common doesn’t mean you should use it. On the contrary, it makes it *worse* that you’re disseminating bad code on Stack Overflow. And no, changing the implementation would *not* be a breaking change: previously correct code will continue to yield the exact same result. – Konrad Rudolph Mar 20 '17 at 11:50
  • 1
    @M.kazemAkhgary And just to throw some authority behind my claim (although this should be completely unnecessary): here’s Eric Lippert, one of the main architects of C#, saying the same thing in clear terms: https://blogs.msdn.microsoft.com/ericlippert/2011/01/31/spot-the-defect-bad-comparisons-part-four/ – Konrad Rudolph Mar 20 '17 at 11:52
  • 1
    Thanks for the link and your valuable information. ill keep that in mind. @KonradRudolph – M.kazem Akhgary Mar 20 '17 at 11:55
  • @M.kazemAkhgary Thank you guys! Noticed these changes as well – Martin Mar 20 '17 at 12:16
2

Write code that does the following:

  1. Declare an array with x * y elements
  2. Populate the entire array with .
  3. Declare a loop with 0.30 * x * y iterations
  4. For each iteration, change a randomly selected element from . to * (you must keep looking until you find one that isn't already a *)
  5. Output the array, x elements per line
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • 3
    you also have to check if the randomly selected element already is a `*` and retry in that case – RoXX Mar 20 '17 at 11:28
  • 1
    This still suffers the problem of not necessarily having 30% of them different. You need to make sure the one you select to change hasn't already been changed. In theory this might only change one entry. – Chris Mar 20 '17 at 11:29
0

Imagine your array to be just 50 elements and forget about the rectangular shape for now. How would you approach that? Declare X dots and 50-X stars, then randomly order them. Like you would randomly order a 1->50 list of numbers.

Now how to randomly order a list of 1->50 numbers? One simple and intuitive way is imagining shuffling cards. Go through the loop and for each position obtain a random number in 1->50. Swap elements chosen (say for i=1 we got random number 7 => swap elements 1 and 7).

Here, you simply need to map this rectangle to those 50 points, which is trivial enough for 2D.

Zizy Archer
  • 1,392
  • 7
  • 11
0

I would randomly select 30% of the possible positions

// create char array
int arrayRows = 5;
int arrayCols = 10;
char[,] arr= new char[arrayRows ,arrayCols];

// populate array with dots
for (int i = 0; i < arrayRows; i++)
{
 for (int j = 0; j < arrayCols; j++)
  {
  arr[i,j] = '.';
  }
}

Random rnd = new Random();

int numberOfPossiblePositions = arrayRows * arrayCols;

int k = 0;
while (k < numberOfPossiblePositions * 0.3) {
 int position = rnd.Next(numberOfPossiblePositions);

 int colIndex = position / 10;
 int rowIndex = position % 10;

 // if the cell already has * try again
 if (arr[rowIndex,colIndex] == '*') {
   continue;
 }

 arr[rowIndex,colIndex] = '*';
 k++;
}
Shiran Dror
  • 1,472
  • 1
  • 23
  • 36
0

Create an array that holds x*y/3 starts and the rest are dots. order by random and iterate through it.

This is the array:

Enumerable.Range(0, count).Select(i => new {Text = "*", Order = rnd.Next() })
     .Concat(Enumerable.Range(0, x*y - count)
     .Select(i=>new { Text = ".", Order = rnd.Next() }))
.OrderBy(i => i.Order).Select(i=>i.Text).ToList();

And this is the code for iteration:

Random rnd = new Random();
int x = 10;
int y = 5;
int count = x*y/3;
var allPlaces =
            Enumerable.Range(0, count).Select(i => new {Text = "*", Order = rnd.Next() })
                .Concat(Enumerable.Range(0, x*y - count)
                .Select(i=>new { Text = ".", Order = rnd.Next() }))
            .OrderBy(i => i.Order).Select(i=>i.Text).ToList();

for (var i = 0; i < x; x++)
{
    for (var j = 0; j < y; j++) { Console.Write(allPlaces[i*j + j]); } 
    Console.WriteLine();
}
Ofir Winegarten
  • 9,215
  • 2
  • 21
  • 27
  • 1
    This is really bad code — please see http://stackoverflow.com/questions/42902328/char-in-random-position/42902493?noredirect=1#comment72904863_42902493 for an explanation. – Konrad Rudolph Mar 20 '17 at 11:53