Функция random()

BigMeister
Offline
Зарегистрирован: 11.02.2018

Уважаемые программисты!

При помощи random() генерирую числа от 1 до 10 и записываю их в массив. Подскажите, как избегать повторов в генерации случайных чисел?

Например, сгенерировалось число 1 и записалось в num[0]. Далее нужно, чтобы генерировались числа от 2 до 10 и тд далее, пока все ячейки массива не будут заполнены разными числами. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

1. Заполняете массив последовательными числами, например, a[i] = i+1;

2. Пробегаетесь циклом по массиву, меняя текущий элемент со случайным местами. swap(a[i], a[random(N)]);

 

BigMeister
Offline
Зарегистрирован: 11.02.2018

Можно поконкретнее про оператор swap ? Компилятор не распознает его.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Это более или менее общепринятое название для функции, которая меняет между собой местами два переданных ей параметра.

Ну, примерно так:

void swap(byte & a, byte & b) {
  byte tmp = a;
  a = b;
  b = tmp;
}

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Мона ещё и так, без промежуточной переменной:


void swap( byte &x, byte &y)
{
		x ^= y;
        y ^= x;
        x ^= y;
}
BigMeister
Offline
Зарегистрирован: 11.02.2018

Разобрался, спасибо.

Буду пытаться интегрировать в код. 

BigMeister
Offline
Зарегистрирован: 11.02.2018

andriano пишет:

Это более или менее общепринятое название для функции, которая меняет между собой местами два переданных ей параметра.

Ну, примерно так:

void swap(byte & a, byte & b) {
  byte tmp = a;
  a = b;
  b = tmp;
}

byte a = 2;
byte b = 4;

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  swap();
}

void swap(byte &a, byte &b)
{
  byte tmp = a;
  a = b;
  b = tmp;
}

При компиляции выводит ошибку "too few arguments to function 'void swap(byte&, byte&)". Что я делаю не так ?

sadman41
Offline
Зарегистрирован: 19.10.2016

На строке 11 не смущает ничего?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

BigMeister пишет:

При компиляции выводит ошибку "too few arguments to function 'void swap(byte&, byte&)". Что я делаю не так ?

Вы указали "too few arguments to function 'void swap(byte&, byte&)".

BigMeister
Offline
Зарегистрирован: 11.02.2018
byte a = 2;
byte b = 4;

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  swap;
  Serial.println(b);
}

void swap(byte &a, byte &b)
{
  byte tmp = a;
  a = b;
  b = tmp;
}

Исправил, но значения a и b между собой не меняются. 

 

sadman41
Offline
Зарегистрирован: 19.10.2016

https://learnc.info/c/functions.html -> передача аргументов.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

BigMeister пишет:

byte a = 2;
byte b = 4;

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  swap;
  Serial.println(b);
}

void swap(byte &a, byte &b)
{
  byte tmp = a;
  a = b;
  b = tmp;
}

Исправил, но значения a и b между собой не меняются. 

 

А с какой стати они должны меняться?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

издеваетесь над человеком )))
нет прямо сказать, что в функцию swap надо передавать аргументы

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ua6em пишет:

издеваетесь над человеком )))
нет прямо сказать, что в функцию swap надо передавать аргументы

А перед этим объяснить, чем функция отличается цикла return.

BigMeister
Offline
Зарегистрирован: 11.02.2018
void setup() 
{
  Serial.begin(9600);
}

void loop() 
{ 
  int b = 4;
  swap(&b);
  Serial.println(b);
}

void swap(int *a)
{
  *a = 2;
}

В com-порт выводится b = 2. Но в чем отличие между тем, если бы я задал a = b ? Ведь это копирование значения одной переменной и подстановка вместо значения другой.

sadman41
Offline
Зарегистрирован: 19.10.2016

Отличие в том, что это не копирование, а прямая работа с областью памяти. См. "Указатели в Си"

b707
Offline
Зарегистрирован: 26.05.2017

BigMeister, какое вообще отношение ваша функция swap() имеет к той, что вам написал andriano? Его функция брала ДВА аргумента и меняла их местами, ваша - берет один и приравнивает его константе 2. Как говорится, "найдите два отличия".

Чего вы нас спрашиваете, какой смысл в вашей функции? это вы ж ее написали...

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DIYMan пишет:

Мона ещё и так, без промежуточной переменной:


void swap( byte &x, byte &y)
{
		x ^= y;
        y ^= x;
        x ^= y;
}


красиво, но извольте пояснить пожалуйста, в какой переменной хранится результат от XOR

так правильно?

x=XOR(x,y);
y=XOR(y,x);
x=XOR(x,y);
 

BigMeister
Offline
Зарегистрирован: 11.02.2018

andriano пишет:

Это более или менее общепринятое название для функции, которая меняет между собой местами два переданных ей параметра.

Ну, примерно так:

void swap(byte & a, byte & b) {
  byte tmp = a;
  a = b;
  b = tmp;
}

 

Я прочел все статьи про передачу аргументов в си, которые нашел. Везде есть примеры про "копирование" (тот код, который я оставил выше) и объяснения к ним. Но нигде нет что-то похожего на то, что Вы написали. 

Можно получить Ваш код с примером целиком ?

sadman41
Offline
Зарегистрирован: 19.10.2016

https://metanit.com/cpp/tutorial/3.3.php -> "Передача параметров по ссылке"

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

BigMeister пишет:

Я прочел все статьи про передачу аргументов в си, которые нашел.

А почему Вы не прочли заодно статьи по Паскалю или там Брейнфаку? Вы уж на каком языке работаете, про тот статьи и читайте.

А в С действительно нет такой передачи параметров - правильно Вы не нашли. Нельзя найти то, чего нет.

strarbit
Offline
Зарегистрирован: 12.06.2016

Добавлю также 1  простой метод: сравнение предыдущего значения с новым случайным значением от генератора. Если значения равны, то сразу вычисляется новое случайное значение. В массив записываете не повторяющие значения.

Image already added
 
 
 
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

strarbit пишет:

Добавлю также 1  простой метод: сравнение предыдущего значения с новым случайным значением от генератора. Если значения равны, то сразу вычисляется новое случайное значение. В массив записываете не повторяющие значения.

Image already added

А если новому равно не предыдущее, а пред-предыдущее, или записанное в массив шагов пять назад, тогда как?

sadman41
Offline
Зарегистрирован: 19.10.2016

Евгений, добейте уже всех - предложите использовать ГСПЧ. Я не математик, у меня красиво не получится, а вот вы сможете...

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

ua6em пишет:

DIYMan пишет:

Мона ещё и так, без промежуточной переменной:


void swap( byte &x, byte &y)
{
		x ^= y;
        y ^= x;
        x ^= y;
}


красиво, но извольте пояснить пожалуйста, в какой переменной хранится результат от XOR

так правильно?

x=XOR(x,y);
y=XOR(y,x);
x=XOR(x,y);
 

В переданных переменных, очевидно. Для такой реализации swap не надо третьей, промежуточной переменной - всё происходит по месту.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Короче, мне кажется, что тут клиника :) И проще отпинаться куском кода - пущай его, что называется: нас, избранных, должно быть мало - зачем увеличивать это количество? :)

byte bArray[10];

void swap( byte &x, byte &y)
{
		x ^= y;
        y ^= x;
        x ^= y;
}

void fill()
{
	const size_t countOfElements = sizeof(bArray)/sizeof(bArray[0]);
	for(size_t i=0;i<countOfElements;i++)
		bArray[i] = i+1;
	
	for(size_t i=0;i<countOfElements;i++)
	{
		swap(bArray[i],bArray[random(0,countOfElements)]);
	}
}

void setup()
{
	fill();
}

void loop()
{
	
}

 

Logik
Offline
Зарегистрирован: 05.08.2014

Так понимаю, следующей темой обсуждения будет вопрос, почему этот код всегда при запуске дает одно и тоже.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Ну надо же как-то заставить читать доку, про тот же randomSeed ;) Да и в этом случае распределение там - такое себе, херовенькое.

strarbit
Offline
Зарегистрирован: 12.06.2016

strarbit пишет:

Добавлю также 1  простой метод: сравнение предыдущего значения с новым случайным значением от генератора. Если значения равны, то сразу вычисляется новое случайное значение. В массив записываете не повторяющие значения.

Image already added

А если новому равно не предыдущее, а пред-предыдущее, или записанное в массив шагов пять назад, тогда как?

[/quote]

Уважаемые ЕвгенийП! Согласна с Вами. В этом случае  имеется вероятность повторяющееся значение. И если это так - это плохой генератор случайных чисел.
Тогда придется сравнивать новое значение от генератора с каждым  ранее записанным значением в массив. Если значения равны,  то вычисляется новое случайное значение.

 
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

strarbit пишет:

strarbit пишет:

Добавлю также 1  простой метод: сравнение предыдущего значения с новым случайным значением от генератора. Если значения равны, то сразу вычисляется новое случайное значение. В массив записываете не повторяющие значения.

Image already added

А если новому равно не предыдущее, а пред-предыдущее, или записанное в массив шагов пять назад, тогда как?

Уважаемые ЕвгенийП! Согласна с Вами. В этом случае  имеется вероятность повторяющееся значение. И если это так - это плохой генератор случайных чисел.
Тогда придется сравнивать новое значение от генератора с каждым  ранее записанным значением в массив. Если значения равны,  то вычисляется новое случайное значение.

 

[/quote]

Этот метод не "простой", а "ужасный": если сложность нормального метода О(N), то у предложенного Вами он явно хуже O(N^2). Вплоть до полного зацикливания. 

И еще, если у генератора вероятность повторного выпадения уже бывших ранее чисел меньше (не говоря о том, что вообще равна 0), чем у еще не выпавших, это никуда не годный генератор.

Logik
Offline
Зарегистрирован: 05.08.2014

У хорошего  ГСЧ в любой момент вероятность выпадания любого числа из диапазона генерируемых не зависит от того, повторное оно или нет, она const и равна 1/М, где М - число возможных значений в диапазоне генерируемых чисел. А так да, подход из #25 верный.

strarbit
Offline
Зарегистрирован: 12.06.2016

andriano пишет:

strarbit пишет:

strarbit пишет:

Добавлю также 1  простой метод: сравнение предыдущего значения с новым случайным значением от генератора. Если значения равны, то сразу вычисляется новое случайное значение. В массив записываете не повторяющие значения.
Image already added

А если новому равно не предыдущее, а пред-предыдущее, или записанное в массив шагов пять назад, тогда как?

Уважаемые ЕвгенийП! Согласна с Вами. В этом случае  имеется вероятность повторяющееся значение. И если это так - это плохой генератор случайных чисел.
Тогда придется сравнивать новое значение от генератора с каждым  ранее записанным значением в массив. Если значения равны,  то вычисляется новое случайное значение.

Этот метод не "простой", а "ужасный": если сложность нормального метода О(N), то у предложенного Вами он явно хуже O(N^2). Вплоть до полного зацикливания.
И еще, если у генератора вероятность повторного выпадения уже бывших ранее чисел меньше (не говоря о том, что вообще равна 0), чем у еще не выпавших, это никуда не годный генератор.
[/quote]

С Вами не согласна. Зациклывания не произойдет от генратора случайных чисел.

Предложенный Вами метод, также сожержит ошибку на которую указал ЕвгенийП. Потому что в Вашем
алгоритме меняете местами заранее изветные значение счетчик-переменная `i` с генерированным значением.
и также не учитывается предыдущее значение генератора чисел.

andriano пишет:

 1. Заполняете массив последовательными числами, например, a[i] = i+1;
 2. Пробегаетесь циклом по массиву, меняя текущий элемент со случайным местами. swap(a[i], a[random(N)]);

unsigned char bArray[10];

void swap( byte &x, byte &y)
{
  x ^= y;
  y ^= x;
  x ^= y;
}

void fill()
{
  const size_t countOfElements = sizeof(bArray) / sizeof(bArray[0]);
  Serial.println("zapolnyaete massiv posledovtelnimi chislami");
  for (size_t i = 0; i < countOfElements; i++) {
    bArray[i] = i + 1;
    Serial.print(bArray[i]);
  }

  Serial.print('\n');
  Serial.println("random");
  for (size_t i = 0; i < countOfElements; i++)
  {
    swap(bArray[i], bArray[random(0, countOfElements)]);
    Serial.print(bArray[i]);
  }
}

void setup()
{
  Serial.begin(9600);
  for (byte i = 0; i < 6; ++i) {
    fill();
    Serial.print('\n');
  }
}

void loop()
{

}

Результат от Вашего не страшного алгоритм

Image already added
 
 
 
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

strarbit пишет:

С Вами не согласна. Зациклывания не произойдет от генратора случайных чисел.

Я не утверждал, что зацикливание непременно произойдет. Но при определенных условиях - может.

Цитата:

Предложенный Вами метод, также сожержит ошибку на которую указал ЕвгенийП.

Отнюдь. Он указал на ошибку в Вашем алгоритме.

Увы, не все умеют правильно записать скетч по краткому словесному описанию алгоритма. Да и в приведенном выше варианте (от которого Вы, по всей видимости, отталкивались) было две ошибки, да и Вы добавили не меньше.

#define arraySize 10
unsigned char bArray[arraySize];

void swap(byte & a, byte & b) {
  byte tmp = a;
  a = b;
  b = tmp;
}

void printArray() {
  for (size_t i = 0; i < arraySize; i++) {
    Serial.print(bArray[i]);
    Serial.print(' ');
  }
  Serial.print('\n');
}

void fill()
{
  Serial.println("zapolnyaete massiv posledovtelnimi chislami");
  for (size_t i = 0; i < arraySize; i++) {
    bArray[i] = i + 1;
  }
  printArray();
  Serial.println("random");
  for (size_t i = 0; i < arraySize; i++)
  {
    swap(bArray[i], bArray[random(0, arraySize-1)]);
  }
  printArray();
}

void setup()
{
  Serial.begin(9600);
  for (byte i = 0; i < 6; ++i) {
    fill();
    Serial.print('\n');
  }
}

void loop()
{

}
zapolnyaete massiv posledovtelnimi chislami
1 2 3 4 5 6 7 8 9 10 
random
5 10 4 6 1 9 7 3 2 8 

zapolnyaete massiv posledovtelnimi chislami
1 2 3 4 5 6 7 8 9 10 
random
5 10 6 2 1 7 8 4 3 9 

zapolnyaete massiv posledovtelnimi chislami
1 2 3 4 5 6 7 8 9 10 
random
8 2 4 7 5 1 6 10 9 3 

zapolnyaete massiv posledovtelnimi chislami
1 2 3 4 5 6 7 8 9 10 
random
5 1 6 10 9 8 4 7 2 3 

zapolnyaete massiv posledovtelnimi chislami
1 2 3 4 5 6 7 8 9 10 
random
4 1 7 2 10 5 9 3 6 8 

zapolnyaete massiv posledovtelnimi chislami
1 2 3 4 5 6 7 8 9 10 
random
9 5 8 2 10 6 3 1 7 4 

 

strarbit
Offline
Зарегистрирован: 12.06.2016
Image already added

Извините, пожалуйста, Мужики! Сделала ошибку, признаю свою ошибку из-за невнимательности. Не заметила, что в массиве переставляются местами элементы массива по индексу, который получен от генератора. Буду, старатся быть внимательнее.

 
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

strarbit, а Вы все-таки напишите по своему "простому методу" скетч. 

Чтобы была возможность сравнить и, главное, - осознать, насколько этот метод ужасен.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

andriano пишет:

Увы, не все умеют правильно записать скетч по краткому словесному описанию алгоритма. Да и в приведенном выше варианте (от которого Вы, по всей видимости, отталкивались) было две ошибки, да и Вы добавили не меньше.

Это какие - укажи плз? Если ты про это (кусок ТВОЕГО кода):

bArray[random(0, arraySize-1)]);

То тут как раз ошибка. Почему - достаточно почитать описание random (брал именно оттуда доку, с оффсайта):

Syntax

random(max)
random(min, max)

Parameters

min - lower bound of the random value, inclusive (optional)

max - upper bound of the random value, exclusive

Выделил жирным. Что тебе ещё не понравилось? Тривиальный способ вычисления размерности массива в виде

sizeof(array)/sizeof(array[0]) ?

Так это совершенно нормальная операция. Возможно, метод обмена с использованием xor?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

BigMeister пишет:

Я прочел все статьи про передачу аргументов в си, которые нашел.

А почему Вы не прочли заодно статьи по Паскалю или там Брейнфаку? Вы уж на каком языке работаете, про тот статьи и читайте.

А в С действительно нет такой передачи параметров - правильно Вы не нашли. Нельзя найти то, чего нет.


То-есть С это классика? Там нет "пойди туда не знаю куда и узнай куда идти дальше"? )))

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

в качестве рандом неплохо брать числа из ряда Пи после запятой (если процессор позволяет)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em, четсно говоря, не понял, отвечу как понял, если что, уточняйте вопрос.

ua6em пишет:

То-есть С это классика?

Это язык программирования. Он не является ни "подмножеством", ни "основой" С++ - это просто другой язык программирования.

ua6em пишет:

Там нет "пойди туда не знаю куда и узнай куда идти дальше"? )))

Не знаю, насчёт указаний куда пойти :))), но там есть стандарт языка, в котором он строго  и формально описан.

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

ua6em, четсно говоря, не понял, отвечу как понял, если что, уточняйте вопрос.

ua6em пишет:

То-есть С это классика?

Это язык программирования. Он не является ни "подмножеством", ни "основой" С++ - это просто другой язык программирования.

ua6em пишет:

Там нет "пойди туда не знаю куда и узнай куда идти дальше"? )))

Не знаю, насчёт указаний куда пойти :))), но там есть стандарт языка, в котором он строго  и формально описан.

 

Женя! Это не подколка и не попытка поймать на слове.

Просто в голову не приходит ничего, что я использую в С, запрещенного в С++. Формально - да, не подмножество... void, прототипы и пр.... но из "обще"-используемого что? Так что по факту - все таки подмножество...

Строго так: Частоиспользуемое подмножество С - является подмножеством С++ ;) ;) ;)... ну, как мне каацо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, это бесконечный религиозный спор, который вести бессмысленно. С точки зрения язычника - это разные языки, а с бытовой - да, пожалуйста. В принципе, с таким же успехом можно сказать, что С в какой-то мере подмножксто Java, например, по крайней мере, у них типа есть "общие подмножества".

Кстати, можно привести примеры вполне часто, я бы даже сказал "постоянно" используемых вещей, которые работают по разному в С и С++. Т.е. синтаксически одинаковы, один и тот же текст компилируется и там, и там, а результат исполнения разный.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Вот снова я был неправильно понят... Я как раз и не хочу никакого спора, тем более, что сам согласен.

Я как раз примеры и хотел, потому, что мне в голову ничего не пришло. Не для спора, а просто "для красоты".

--------------------

А что да "как язычнику" ;), так я ж говорил, что в логику пришел от формальных грамматик и синтаксического анализа. Это немного другая сторона языков программирования, но не слишком далекая от вашей.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Щас сделаю пример.

Пихаем в директорию "kaka" два файла.

Файл kaka.ino (компилируется как С++)

extern "C" { const char * kaka2(); }

const char * kaka1() {
	return sizeof('a') == 1 ? "C++" : "C";	
}

void setup(void) {
	Serial.begin(57600);
	Serial.println(kaka1());
	Serial.println(kaka2());
}

void loop(void) {}

файл kakaс.c (компилируется как С)

const char * kaka2() {
	return sizeof('a') == 1 ? "C++" : "C";	
}

Как видим, функции kaka1 и kaka2 абсолютно идентичны, только компилируются первая как С++, а вторая как С.

Из-за того, что в С символьная константа имеет тип int, а в С++ - char (причём, это требование языка, а не прихоть компилятора), в результате, в мониторе порта, видим:

C++
C

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ну Женя! Про преобразование к int ты еще с Архатом, года три назад, дискутировал... Ему очень тогда стандарт не понравился! ;) ;) ;)...

Это простой пример. Верю, что общеизвестный... хотя... ;) Я надеялся на что-то более неочевидное.

Все равно - спасибо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Я то как раз из кожи напрягался, чтобы сделать пример как можно проще, т.к. считал, что неочевидное будет названо надуманным :)))

Неочевидное можно, мне и самому интересно сделать,  но только завтра, у меня ДР сегодня, пора к пьянке готовиться. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

От всей души поздравляю. 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Евгений! Удачи, денег, здоровья! От души!

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Спасибо, парни!

Efim
Offline
Зарегистрирован: 04.05.2018

С днем рождения Женя!

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Евгений Петрович! Здоровья и кавказского долголетия!!!

strarbit
Offline
Зарегистрирован: 12.06.2016

Евгений Петрович!
С днем рожденья Вас поздравляю
Здоровья, радости и везенья!

Image already added