Random для Attiny13

Otto
Offline
Зарегистрирован: 26.06.2016

Здравствуйте. Вопрос, есть вот такой компактный кусок кода для Attiny13, который занимает мало места и достаточно хорошо генерирует псевдо-рандом, выдавая значения от 0 до 7:

byte rnd() {
 static uint16_t seed;
 seed = (seed * 2053ul) + 13849;
 return (seed >> 8) & 7;
}

Как его изменить, что бы числа выпадали от 1 до 6?

 

Понимаю, что после битового И в конце можно поставить 6 и тогда считает до шести, но от нуля.

Как сделать сдвиг или доп. условие что бы счёт шёл от одного до шести.

 

Спасибо.

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

сравнивать на 0 и запускать генерацию по новой, по другому наврядли

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Да нифига нельзя ставить 6. Тут 7 это маска, где три младших бита равны единицам. А если вместо 7 воткнуть 6, то результаты будут 2,4,6 и все. Поэтому разумный совет при нуле или семи перезапрашивать результат у процедуры. 

Или, как уже сказали, выкинуть операции сдвига и наложения битовой маски и использовать остаток от деления на 6 (%6) , тогда будешь получать значения от 0 до 5 и добавлять единицу.

Otto
Offline
Зарегистрирован: 26.06.2016

Спасибо за помощь. Поигравшись с цифрами в итоге сделал так:

byte rnd() {
  static uint16_t seed;
  seed = (seed * 2053ul) + 13849;
  return ((seed >> 7) % 6) + 1;
}

Проследив за значениями, повторения получились достаточно рандомные.

 

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

Otto пишет:

Спасибо за помощь. Поигравшись с цифрами в итоге сделал так:

byte rnd() {
  static uint16_t seed;
  seed = (seed * 2053ul) + 13849;
  return ((seed >> 7) % 6) + 1;
}

Проследив за значениями, повторения получились достаточно рандомные.

а что статистика говорит?

byte rnd() {
  static uint16_t seed;
  seed = (seed * 2053ul) + 13849;
  return ((seed >> 7) % 6) + 1;
}

uint16_t statistic[7];
byte j;

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

void loop() {
  for (uint16_t i = 0; i < 65535; i++)
  {
    j = rnd();
    statistic[j]++;
  }

  Serial.print("m1 = ");
  Serial.println(statistic[1]);
  Serial.print("m2 = ");
  Serial.println(statistic[2]);
  Serial.print("m3 = ");
  Serial.println(statistic[3]);
  Serial.print("m4 = ");
  Serial.println(statistic[4]);
  Serial.print("m5 = ");
  Serial.println(statistic[5]);
  Serial.print("m6 = ");
  Serial.println(statistic[6]);
  delay(1000);
}

 

Otto
Offline
Зарегистрирован: 26.06.2016

Всё же вернул сдвиг на 8, так более равномерное выпадание.

Вот что получилось за 10 прогонов:

m1 = 11007
m2 = 11008
m3 = 11008
m4 = 11008
m5 = 10752
m6 = 10752

 
m1 = 22015
m2 = 22016
m3 = 22016
m4 = 22015
m5 = 21504
m6 = 21504

 
m1 = 33023
m2 = 33024
m3 = 33024
m4 = 33022
m5 = 32256
m6 = 32256

 
m1 = 44031
m2 = 44032
m3 = 44032
m4 = 44030
m5 = 43008
m6 = 43007

 
m1 = 55039
m2 = 55040
m3 = 55040
m4 = 55038
m5 = 53760
m6 = 53758

 
m1 = 511
m2 = 512
m3 = 512
m4 = 510
m5 = 64511
m6 = 64510

 
m1 = 11519
m2 = 11519
m3 = 11520
m4 = 11518
m5 = 9727
m6 = 9726

 
m1 = 22527
m2 = 22526
m3 = 22528
m4 = 22526
m5 = 20479
m6 = 20478

 
m1 = 33535
m2 = 33534
m3 = 33535
m4 = 33534
m5 = 31231
m6 = 31230

 
m1 = 44543
m2 = 44542
m3 = 44543
m4 = 44542
m5 = 41983
m6 = 41981

Каждые 6-7 прогонов есть сильно отличающиеся результаты, но для моих целей это не так важно. Большое спасибо за помощь.

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

на большом количестве генераций цифры 5 и 6 выпадают реже

Upper
Offline
Зарегистрирован: 23.06.2020

Рандомность это не только количество значений, но куча других критериев. Честно говоря не разобрался как запускать существующие тесты.

Для данного алгоритма - если учитывать только количество значений, то лучше чем >>7 и >>8 получается если

return ((seed / 254) % 6) + 1);

А еще ровнее если вообще не делить

return ((seed % 6) + 1);

Otto
Offline
Зарегистрирован: 26.06.2016

Столкнулся с проблемой о которой не подумал изначально. Сброс Attiny13 идёт через Reset, а следовательно и  псевдо-рандом всегда начинается с одних и тех же значений, так как код выполняется 1 раз и Attiny уходит в вечный сон до сброса.

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

Можно считывать значение с EEPROM, произвести расчёт рандомного числа, затем прибавить +1 и потом записать в EEPROM.

Вот только проблема, не получается всё это совместить, есть идеи у кого нибудь?

Upper
Offline
Зарегистрирован: 23.06.2020

Не понял, что не удается совместить. Вы вроде все расписали. Храните счетчик запусков в ячейке, при старте увеличиваете на 1.
Так как математики из нас никакие, то чисто опытным путем посмотрите - устроит ли вас рандомность если просто брать начальный seed не всегда 0 а последовательно 0, 1, 2, 3 ... 255, 0, 1 ...

Otto
Offline
Зарегистрирован: 26.06.2016

Подумав, получилось вот так:

    uint16_t seed;
    uint16_t seed2 = EEPROM.get(1, seed);
    seed = seed2;
    seed = (seed * 2053ul) + 13849;
    EEPROM.put(1, seed);
    seed = ((seed >> 8) % 6) + 1; //более менее


    Serial.print("rnd: ");
    Serial.println(seed);
    Serial.print("EEPROM: ");
    Serial.println(seed2);

Квалификатор static убрал, так как код будет выполняться 1 раз и далее вечный сон до перезагрузки, а 22 байта лишними не будут. Вот такие значения более менее рандомные после десятка прогона.
Вот такая картина получилась при запуске с пустой ячейки EEPROM:

m1 = 0
m2 = 1
m3 = 1
m4 = 2
m5 = 1
m6 = 1

m1 = 1
m2 = 1
m3 = 2
m4 = 2
m5 = 3
m6 = 3

m1 = 2
m2 = 2
m3 = 3
m4 = 3
m5 = 4
m6 = 4

m1 = 2
m2 = 2
m3 = 3
m4 = 7
m5 = 4
m6 = 6

m1 = 2
m2 = 5
m3 = 4
m4 = 7
m5 = 4
m6 = 8

m1 = 3
m2 = 6
m3 = 6
m4 = 7
m5 = 6
m6 = 8

m1 = 5
m2 = 6
m3 = 7
m4 = 8
m5 = 7
m6 = 9

m1 = 6
m2 = 8
m3 = 8
m4 = 9
m5 = 7
m6 = 10

m1 = 7
m2 = 10
m3 = 8
m4 = 11
m5 = 8
m6 = 10

m1 = 9
m2 = 10
m3 = 9
m4 = 13
m5 = 8
m6 = 11

m1 = 10
m2 = 11
m3 = 12
m4 = 13
m5 = 8
m6 = 12

m1 = 11
m2 = 13
m3 = 14
m4 = 13
m5 = 9
m6 = 12

Использовал EEPROM: get и put вместо read и write, так хоть и больше занимается Flash, тем не менее проще разместить большие числа в EEPROM и рандом более равномерный.
Если есть замечания, буду рад им.

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

Otto пишет:

Можно считывать значение с EEPROM, произвести расчёт рандомного числа, затем прибавить +1 и потом записать в EEPROM.

Вы забыли написать, что именно Вы собираетесь записывать в EEPROM. Судя по тому, что собираетесь прибавлять по 1, совсем не то, что нужно.

А нужно - seed.