Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Random


Автор: Retro 3.5.2007, 18:58
На написание данного кода меня подвигнул участник форума - Legolas. Ему требовалось перемешать строки в файле случайным образом. Поскольку меня привлекает работа с цифрами в общем и с "случайными числами" в частности я честно подумал над вопросом, поискал в интернете код для .NET и наткнулся на пустоту, точнее были некие варианты но настолько закрученные, что я сомневался в их пригодности. Задача сводилась к тому как получить случайные числа для некого интервала, причем без их повторения. Ничего не найдя в интернете(возможно не так или не там искал) я задался вопросом, а зачем нужны многочисленные проверки, зачем генерировать случайное число если оно уже выпало. Так родился следующий код с использованием удобных методов ArrayList'a:

Код
// Список, в котором будут находиться данные необходимые для работы
List<string> rndArr = new List<string>();
// Интервал для случайных чисел (количество элементов в массиве)
int n;
// Индекс текущего элемента
int rndInt;
// Наш генератор случайных чисел
Random rnd = new Random();

// Заполняем список данными, например, строками
for (int i = 0; i < 10; i++)
{
    rndArr.Add(i.ToString());
}
// Количество элементов в массиве
n = rndArr.Count;

/**********************************************/

do
{
    // rndInt получает случайное число в интервале от 0 до n (количество элементов)
    rndInt = rnd.Next(0, n);
    // Выполняем необходимое действие с элементом массива (записываем в файл, выводим в консоль или текстбокс и т.д.)
    this.myTextBox.Text += rndArr[rndInt].ToString() + " ";
    // Удаляем элемент списка, который уже выпал случайным образом и был использован по назначению
    rndArr.RemoveAt(rndInt);
    // Уменьшаем размер списка до нынешнего состояния (без выпавшего элемента)
    // Просто для наглядности
    rndArr.TrimToSize();
    // Уменьшаем n до размера списка
    n = rndArr.Count;
    //Продолжаем проходы по циклу до тех пор, пока элементы не кончатся
} while (rndArr.Count != 0)

Автор: Exception 3.5.2007, 20:50
Более универсальный вариант:

Код
using System;
using System.Collections.Generic;
using System.Text;


public static class StringRandomizer
{
    private static List<string> SplitString (string input, string separator)
    {
        List<string> result = new List<string> ();
        foreach (string part in input.Split (separator))
        {
            result.Add (part);
        }
        return result;
    }


    public static string Randomize (string input, string separator)
    {
        List<string> parts = SplitString (input, separator);
        StringBuilder sb = new StringBuilder (input.Length);
        Random rnd = new Random ();
        int length = parts.Length;
        for (i = 0; i < length; i++)
        {
            int num = rnd.Next(0, length);
            sb.Append (parts[num] + separator);
        }
        return sb.ToString ();
    }

    public static string RandomizeLines (string input)
    {
        return StringRandomizer.Randomize (input, '\n');
    }

    public static string RandomizeWords (string input)
    {
        return StringRandomizer.Randomize (input, ' ');
    }
}


Пример:

Код

string bla = "а б в г";
Console.WriteLine (StringRandomizer.RandomizeWords (bla)); // тасует слова с случайном порядке


Код писался в браузере, так что возможна необходимость подточить его напильником smile .

Автор: Retro 3.5.2007, 21:42
Exception, весь прикол был в том, чтобы заданные элементы не повторялись. Я немного "подточил твой код напильником" smile чтобы запустить. Ввел предложенный тобой пример:
Код

string bla = "а б в г";
Console.WriteLine (StringRandomizer.RandomizeWords (bla)); // тасует слова с случайном порядке

Он таки да тасует слово(зачем нужно пробел использовать?) только значения повторяются.
Например "г б г г". smile

Добавлено через 5 минут и 59 секунд
Exception, че-то у меня не получается со строками. Можно пример?

Автор: Legolas 4.5.2007, 00:09
Отличный код, спасибо Retro!  smile 

Автор: Naum 4.5.2007, 10:41
Цитата(Retro @  3.5.2007,  19:58 Найти цитируемый пост)
точнее были некие варианты но настолько закрученные,

Да уж привычка - это плохо.  smile Я как-то давно придумал похожий способ и всегда, в подобных ситуациях пользую его. И только сейчас обратил внимание насколько мой метод убогий. У меня из массива ничего не удаляется, а существует второй булевский массив, в котором true или false в зависимости использовался ли уже элемент с таким же индексом в оригинальном массиве. Я лопух.  smile 
Retro, лови +1. 

Автор: Exception 4.5.2007, 20:10
Цитата(Retro @  3.5.2007,  22:42 Найти цитируемый пост)
Он таки да тасует слово(зачем нужно пробел использовать?) только значения повторяются.


Тьфу smile .
Знал же, что о чём-нибудь забуду.

Цитата(Retro @  3.5.2007,  22:42 Найти цитируемый пост)
Exception, че-то у меня не получается со строками. Можно пример? 


Теоретически,

Код

string stuff = 
     @"Hello
         cruel
         world";
Console.WriteLine (StringRandomizer.RandomizeLines (stuff));


Практически, у меня нет компилятора уже две недели (и вряд ли скоро появится smile ).

Автор: Retro 4.5.2007, 23:08
Цитата(Exception @  4.5.2007,  19:10 Найти цитируемый пост)
Практически, у меня нет компилятора уже две недели (и вряд ли скоро появится

Ужос! smile 

Автор: sergejzr 4.5.2007, 23:46
Мы решили эту проблему похожим способом, но он мне кажется более оптимальным.

1) Создать массив неповторяющихся N чисел. (проинициализировав массив значениями min до max подряд) N=max-min
2) выбрать число по случаиному индексу массива i_rand ot rand(0,N)
3) переписать значение по индеху  i_rand последним числом массива (числом по индеху N-1)
4) сделать N--; 
5) GOTO 2;

Таким образом не нужно никаких сторонних функций кроме rand() В этом единственное отличие от твоего способа.

Автор: Exception 5.5.2007, 18:08
Код

using System;
using System.Collections.Generic;
using System.Text;
public static class StringRandomizer
{
    // функциональные наворотеги :)
    private delegate R FoldFunction<T1, T2, R>(T1 a, T2 b);
    private static S Fold<T, S>(FoldFunction<T, S, S> f, S init,
IEnumerable<T> list) {
        S s = init;
        foreach (TList e in list) {
            s = f (e, s);
        }
        return s;
    }

    private static List<string> SplitString (string input, string separator)
    {
        List<string> result = new List<string> ();
        foreach (string part in input.Split (separator))
        {
            result.Add (part);
        }
        return result;
    }
    public static string Randomize (string input, string separator)
    {
        List<string> parts = SplitString (input, separator);
        Random rnd = new Random ();
        int current = parts.Length - 1;
        while (current > 0)
        {
            int num = rnd.Next (0, current);
            string tmp = parts[current];
            parts[current] = parts[num];
            parts[num] = tmp;
            current--;
        }
        return StringRandomizer.Fold<string, string>
        (
          delegate (string val, string acc) { return acc + separator + val; },
          "",
          parts
        );
    }
    public static string RandomizeLines (string input)
    {
        return StringRandomizer.Randomize (input, '\n');
    }
    public static string RandomizeWords (string input)
    {
        return StringRandomizer.Randomize (input, ' ');
    }
}


Должно работать (алгоритм Сергея).
Прошу прощения за выпендрёж с Fold smile .

Автор: STRELOKBMSTU 13.8.2007, 23:42
Код

 StringRandomizer.RandomizeWords (bla)); 


И в каком пространстве имен находится эта удивительная функция??? У меня комп ее не видит(

Автор: Retro 14.8.2007, 09:16
Цитата(STRELOKBMSTU @  13.8.2007,  22:42 Найти цитируемый пост)
И в каком пространстве имен находится эта удивительная функция??? У меня комп ее не видит

Жесть какая. smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)