2016-07-27 3 views
1

Я пытаюсь реализовать моделирование методом Монте-Карло в приложении формы C# с использованием Взвешенный быстрый союз с контуром пути. Во-первых, я беру gridSize, а затем генерировать сетки равен gridSize * gridSize и состоит из черного фона этикетки, которые представляют сайты, то я случайно союз некоторые сайты и сделать свой фон в белый цвет. Проблема в том, что всякий раз, когда я запускаю проект, я получаю все сайты в черном - независимо от того, сколько раз я пытаюсь -, но если бы я добавил точки останова и шаг за шагом, я получаю несколько белых сайтов по назначению !!!Странное поведение при реализации моделирования monte carlo в C#

Вот код:

public partial class Form1 : Form 
{ 
    int gridSize = 0; 
    byte siteSize = 50; 
    FlowLayoutPanel flowLayoutPanel; 
    UnionFind uf; 
    int randomFirstNumber; 
    int randomSecondNumber; 
    string labelName = string.Empty; 
    Random rnd = new Random(); 
    int numberOfAttempts; 
    Label label; 
    HashSet<int> excludeHashSet = new HashSet<int>(); 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void setGridSizeButton_Click(object sender, EventArgs e) 
    { 
     if (!string.IsNullOrEmpty(gridSizeTextbox.Text) && int.TryParse(gridSizeTextbox.Text, out gridSize)) 
     { 
      if (gridSize > 0) 
      { 
       var flowLayoutPanel = createFlowLayoutPanelAndLabels(); 

       setGridSizeButton.Enabled = false; 
       gridSizeTextbox.Enabled = false; 

       randomlyUnionSitesAndWhiteThem(); 
      } 
     } 
    } 

    private void reset_Click(object sender, EventArgs e) 
    { 
     setGridSizeButton.Enabled = true; 
     gridSizeTextbox.Enabled = true; 
     gridSizeTextbox.Text = string.Empty; 
     flowLayoutPanel.Dispose(); 
     excludeHashSet = new HashSet<int>(); 
     gridSizeTextbox.Focus(); 
    } 

    private FlowLayoutPanel createFlowLayoutPanelAndLabels() 
    { 
     flowLayoutPanel = new FlowLayoutPanel(); 
     flowLayoutPanel.Name = "flowLayoutPanel"; 
     flowLayoutPanel.Location = new Point(30, 60); 
     flowLayoutPanel.Size = new Size(gridSize, gridSize); 
     flowLayoutPanel.BorderStyle = BorderStyle.Fixed3D; 
     flowLayoutPanel.Width = siteSize * gridSize + 10 * gridSize; 
     flowLayoutPanel.Height = siteSize * gridSize + 10 * gridSize; 

     for (int i = 0; i < gridSize * gridSize; i++) 
     { 
      var label = new Label(); 
      label.Text = ""; 
      label.Name = i.ToString(); 
      label.Width = siteSize; 
      label.Height = siteSize; 
      label.BackColor = Color.Black; 
      label.Margin = new Padding(3); 
      label.BorderStyle = BorderStyle.FixedSingle; 

      flowLayoutPanel.Controls.Add(label); 
     } 
     this.Controls.Add(flowLayoutPanel); 

     return flowLayoutPanel; 
    } 

    private void randomlyUnionSitesAndWhiteThem() 
    { 
     uf = new UnionFind(gridSize * gridSize); 

     numberOfAttempts = rnd.Next(0, gridSize * gridSize); 
     for (int i = 0; i < numberOfAttempts; i++) 
     { 
      randomFirstNumber = getRandomNumber(0, gridSize * gridSize, excludeHashSet); 
      randomSecondNumber = getRandomNumber(0, gridSize * gridSize, excludeHashSet); 

      labelName = uf.union(randomFirstNumber, randomSecondNumber).ToString(); 
      if (Convert.ToInt16(labelName) > -1) 
      { 
       label = (Label)flowLayoutPanel.Controls.Find(labelName, false).First(); 
       label.BackColor = Color.White; 
       excludeHashSet.Add(Convert.ToInt16(labelName)); 
      } 
     } 
    } 

    private int getRandomNumber(int min, int max, HashSet<int> excludes) 
    { 
     int randomNumber; 
     do 
     { 
      var range = Enumerable.Range(min, max).Where(i => !excludes.Contains(i)); 

      var rand = new Random(); 
      int index = rand.Next(0, max - excludes.Count); 

      randomNumber = range.ElementAt(index); 
     } 
     while (excludes.Any(x => x == randomNumber)); 

     return randomNumber; 
    } 
} 

И это мой Union метод:

class UnionFind 
{ 
    ..... 
    public int union(int firstNumber, int secondNumber) 
    { 
     firstNumber = getRoot(firstNumber); 
     secondNumber = getRoot(secondNumber); 

     if (firstNumber == secondNumber) 
     { 
      return -1; 
     } 

     if (sizes[firstNumber] < sizes[secondNumber]) 
     { 
      array[firstNumber] = secondNumber; 
      sizes[secondNumber] += sizes[firstNumber]; 

      return array[firstNumber]; 
     } 
     else 
     { 
      array[secondNumber] = firstNumber; 
      sizes[firstNumber] += sizes[secondNumber]; 

      return array[secondNumber]; 
     } 
    } 
    ..... 
} 

ответ

0

Я выяснил причину странного поведения. Это происходит при многократном использовании класса Random внутри цикла. Это можно решить несколькими способами.

Например, System.Threading.Thread.Sleep(10);. Здесь вы делаете петлю сон для n миллисекунды.

private int getRandomNumber(int min, int max, HashSet<int> excludes) 
{ 
    int randomNumber; 
    do 
    { 
     System.Threading.Thread.Sleep(10); 
     ... 

Это также может быть достигнуто с использованием System.Timers.Timer, который позволит получить тот же результат. Также этот метод более подходит для такой проблемы.

System.Timers.Timer myTimer = new System.Timers.Timer(); 

private void percolateEverySecond() 
{ 
    myTimer.Elapsed += new ElapsedEventHandler(percolateForTimer); //this is the function that will be executed every myTimer.Interval 
    myTimer.Interval = 1000; 
    myTimer.Enabled = true; 
} 

private void percolateForTimer(object source, ElapsedEventArgs e) 
{ 
    //randomly white site, then union it with surrounding sites 
    ... 
} 

private void Start_Click(object sender, EventArgs e) 
{ 
    ... 
    percolateEverySecond(); 
} 

См this для получения дополнительной информации о System.Threading.Thread.Sleep() против System.Timers.Timer.

Также обратите внимание, что приведенный выше код в вопросе имеет огромную возможность для улучшения, но, как правило, вы должны сделать каждую функцию ответственной только за одну конкретную задачу «Single Responsibility Principle».

+1

Итак, по существу Алекс был прав, даже если объяснение пропустило некоторые детали.И используя один экземпляр «Случайный» вместо нового, основанного на текущем времени, другой новый, основанный также на (неизмененном) текущем времени, кажется гораздо лучшим исправлением, чем произвольно выбранные задержки, чтобы предотвратить то же время от наблюдения дважды. – hvd

0

Вы просто должны позвонить в this.Refresh() метод в конце вашего randomlyUnionSitesAndWhiteThem() method;)

+0

К сожалению, это не решило проблему. – Ahmed

1

Вы объявили новый случайный объект в методе getRandomNumber, который всегда дает вам то же значение; если вы используете свой атрибут rnd, то это работает.

Here is the explanation

+0

Я попробовал, и это не решило проблему. Я не получаю случайные числа в одних и тех же значениях, каждый раз получаю разные значения. Моя проблема в том, что я получаю желаемое поведение, если я добавил точки останова и выполнил код по строкам, но если я просто запустил программу - без очереди по очереди - я получаю все сайты в черном, что означает, что они не получили союзно-е изд. Это означает, что я получаю 2 поведения/результатов для одного и того же кода. Я хочу знать, почему и как это исправить. – Ahmed

+0

Алекс прав. New Random() создает новое Случайное, основанное на текущем времени. Запуск с контрольными точками замедляет все так сильно, что вы получаете разные случайные –

Смежные вопросы