Не могу придумать формулу. Задача несколько специфическая. Предположим, что у нас есть некоторая переменная, которая может хранить число из диапазона от 0 до 9.
Надо придумать как умножить такие переменные.
Диапазон следует представлять как замкнутую ленту из последовательных ячеек 0, 1, 2, ..., 9, таким образом если мы пишем 9+1, то попадаем в ячейку 0 (т.е. 9+1=0), и так далее вплоть до 9+9=8.

Дано:
max = 9 - это предельное значение диапазона,
a - одна переменная,
b - другая переменная
carry - переменная переноса.

Операции:
+, -, *, /
битовые |, &, ^, ! (or, and, xor, not)
% - взятие остатка
ну и любые другие можно использовать

Примеры:
a=1, b=9 => carry = 0
a=2, b=9 => carry = 1
a=4, b=8 => carry = 3

Ограничения:
переменных способных хранить числа, состоящие более, чем из одного знака, не существует. Это означает, что операция 2*9 невыполнима в стандартном понимании, так как не существует переменной способной хранить число 18, результатом операции 2*9 будет 2*9=9+9=8 как уже говорилось. Данное ограничение можно воспринимать как своеобразную архитектуру процессора, который работает только лишь с однозначными числами.

Найти:
функцию carry = f(a,b,max).

Например, при 2*9 нахождение carry=1 позволит отпечатать произведение как последовательная печать переменной carry как 1 и печать операции 2*9 как 8, итого 18.

@темы: Головоломки и занимательные задачи, Дискретная математика

Комментарии
29.11.2020 в 00:04

Trotil, по идее так же должно работать, а выдает фигню какую-то...


29.11.2020 в 00:20

Trotil, кажется я близок



Вывод
mul: fefe 1

Должно быть:
mul: fffe 1
29.11.2020 в 01:28

Trotil, спасибо, отдохнул чуть, попробовал снова и получилось.



Думаю, что ужать код ещё можно.

p.s.
хотя нет, неправильно при
uint16 b[] = { 0xaaui16, 0xffui16 }; // 43775
считает
29.11.2020 в 03:36

Trotil, вроде бы так


29.11.2020 в 07:09

Работает, это хорошо.
Но тебя вроде просили реализовать умножение двух 32-битных чисел, а не 16-битных.

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

Сумматоры - это чисто комбинационная логика и легко переносятся на Си (в отличие от схем с предыдущем состоянии на входе - типа триггеров).
И алгоритмы в книжке самые оптимальные. Поэтому и порекомендовал.

А если её всю прочитать и проделать лабы - это ключ к сакральному знанию о том, как делается сейчас в мире в корпоративных гигантах вся современная продвинутая цифровая электроника на низком уровне.
29.11.2020 в 11:40

Trotil,
Работает, это хорошо.

походу не работает из-за неправильной реализации сложения, сейчас проснулся и заметил, что
случай 65535 + 1 вместо
sum: 1 0
у меня покажет
sum: 0 0,
причём я понимаю почему так, но пока не понимаю как это поправить...



Но тебя вроде просили реализовать умножение двух 32-битных чисел, а не 16-битных.

да, всё так, но умножение 32-битных будет базироваться на сложении и умножении 16-битных, поэтому пока операции с ними и обкатываю
29.11.2020 в 11:58

да, всё так, но умножение 32-битных будет базироваться на сложении и умножении 16-битных, поэтому пока операции с ними и обкатываю


Ок. Но я бы сделал:

a) функцию умножения 32-битного числа на A < 256,
b) функцию сдвига 32-битного числа на b бит
с) цикл, в котором реализуется алгоритм умножения столбиком
(сложение у тебя уже есть)

Это вроде как проще запрогать, и меньше шансов запутаться и допустить ошибку.
29.11.2020 в 12:11


причём я понимаю почему так, но пока не понимаю как это поправить...


удобнее представлять, что у тебя 256-ричная система счисления.
попробуй на бумаге сложить и не 16-битные числа, а чё-нить подлиннее, выйдет универсальный алгоритм

Хотя мб тебе проще твоим подходом добить до конца
29.11.2020 в 12:32

Trotil, поправил



Но есть сомнения в этом куске


На примере в комментариях carry это переполнение при сложении ffee + 5600, в общем случае y1 (ffee) + y2 (ff56), но y2 будет всегда же сдвинут на байт влево по алгоритму умножения столбиком, тогда будет ffee + 5600, в общем случае y1 + (y2 << 8), причём младший байт y1 это ee, в общем случае (y1 & 0x00ff), а младший байт y2 это всегда 00, поэтому их сумма никогда не должна давать переполнения в старший байт, а тогда вроде бы достаточно просто так записать:



то есть не учитывать сумму ( (y1 & 0x00ff) + ((y2 << 8) & 0x00ff) ) >> 8
29.11.2020 в 12:59

Trotil, умножение всё-таки рабочее было по причинам описанным выше, а вот исправленное сложение (переполнение из-за младшего байта типа 65535+1) падает



Вывод:
sum: 2 9998

Должно быть:
sum: 1 9998

То есть к переносу по старшему байту добавляет перенос по младшему байту... и получается неверный перенос
29.11.2020 в 13:31

Trotil, всё, зафурычило наконец-то :dance:


29.11.2020 в 14:44

Как-то так ... Осталось функцию mul_32() реализовать


29.11.2020 в 15:02

Trotil, что-то не пойму, формула же по идее такая должна быть b*d + ((a*d + b*c) << 16) + (a*c << 32) ? Но сдвиг на 16 и 32 это сразу 0 ... Хотя это позиция в результирующем массиве...


29.11.2020 в 16:01

Трешак какой-то получается, надо думать как ужимать


29.11.2020 в 16:50

Трешак какой-то получается

Груша Вильямс, именно поэтому придумали массивы, функции и циклы!
29.11.2020 в 17:10

Trotil, всю голову сломал, но так и не придумал как это в цикл обернуть, 100% в цикл оборачивают
29.11.2020 в 17:35

Trotil, с циклом проблема собственно какая, проблема в самом алгоритме умножения в столбик
например,
ab
cd

1) b*d и от него carry_bd и res[3]

2a) a*d и от него carry_ad и 1-ый res[2]
2б) b*c и от него carry_bc и 2-ой res[2]
2в) a*d + b*c и от него carry_ad_bc и 3-ий res[2] = 1-ый res[2] + 2-ой res[2]
2г) a*d + b*c + carry_bd и от него carry_ad_bc_carry и 4-ый res[2] = 3-ий res[2] + carry_bd

3) как и два можно сказать...

Непонятно как такую логику зацикливать, в первую очередь непонятно с переменной carry, возможно с одной переменной это провернуть или нет, по-моему с одной нельзя, напрашиваются carry_add на сложение и carry_mul на умножение, та же проблема с res[2] на разных шагах

Перепишу с carry_add и carry_mul:

1) b*d и от него carry_mul и res[3]

2a) a*d и от него новый carry_mul -- выходит, что предыдущее значение carry_mul перетирается
2б) b*c и от него новый carry_mul -- проблема как и на предыдущем шаге
2в) a*d + b*c и от него carry_add -- тут проблем нет
2г) a*d + b*c + (нужен самый первый carry_mul, тот который carry_bd, тогда получается, что carry_mul на шаге 1 и carry_mul на шаге 2 должны быть разные) и от него carry_add суммируется с предыдущим значением carry_add (т.е. carry_add += carry_add)

3) как и два можно сказать...
29.11.2020 в 18:15

Trotil, как бы общий какой-то цикл могу, конечно, написать, что-то типа этого



а дальше собственно ступор...
29.11.2020 в 18:35

завтра подкину мыслей.
29.11.2020 в 19:24

Trotil, спасибо
xx98
xx76
______
xx48
x56
x54
63
_______
из-за того, что 54 не сдвинуто на знак относительно предыдущего частичного произведения 56, получается, что общая логика рушится, то есть для элементов a[1]*b[0] и a[0]*b[1] будет немного другая логика, без смещения на знак, в этом и проблема основная как мне кажется. Но каждое из частичных произведений (48, 56, 54 и 63) является числом типа uint16, которое легко получить через ту же функцию mul_16(). В принципе в цикле легко собрать массив этих частичных произведений, а потом уже как-то с ним там работать. Вроде как это упрощение задачи уже будет, как минимум переход от умножения к сложению произошёл.
29.11.2020 в 21:45

Trotil, попутно выяснилось, что функция mul_16() неправильно работает -- отталкивался как вы и говорили от умножения на число, собственно умножение на число и реализовал неправильно

Пример


Надо переполнение при подсчёте y1 как-то отловить, оно возникает как раз при сложении...
29.11.2020 в 22:55

Trotil, mul_16() поправил



частичные произведения написал


30.11.2020 в 09:08

За час набросал идею, которую я советовал: ideone.com/CdQR8z
Вроде работает.
Если нажать fork, можно редактировать и запускать старый код там.

P.S. Через ООП получилось бы компактнее, но ведь нужно было на Си.
05.12.2020 в 09:18

Какой подход к решению задачи лучше на твой взгляд?