Функция линейного преобразования

Мне нужно написать функцию, которая принимает 4 байта в качестве входных данных, выполняет обратимое линейное преобразование и возвращает его как 4 байта.

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

Проблемы:

  • если я использую умножение, он не будет обратимым после того, как он будет модифицирован 255 через хранилище в виде байта (и его нужно оставить в виде байта)
  • если я использую дополнение, оно не может быть обратимым и распределительным

Одно решение: Я мог бы создать массив байтов длиной 256 ^ 4 и заполнить его в сопоставлении один к одному, это сработает, но есть проблемы: это означает, что мне нужно искать граф размером 256 ^ 8 из-за необходимости поиска для бесплатных чисел для каждого значения (следует отметить, что распределенность должна быть sudo random на основе массива байтов 64 * 64). У этого решения также есть НЕМНОГО (смеется) проблема, связанная с необходимостью 8 ГБ ОЗУ, что делает это решение бессмысленным.

Область ввода такая же, как область вывода, каждый ввод имеет уникальный вывод, другими словами: сопоставление один-к-одному. Как я отмечал в разделе «Одно решение», это очень возможно, и я использовал этот метод, когда речь шла о меньшем домене (всего 256). Дело в том, что по мере того, как цифры становятся большими, этот метод становится чрезвычайно неэффективным, дефектом дельты был O(n^5), а у omega был O(n^8) с аналогичной неэффективностью использования памяти.

Мне было интересно, есть ли какой-нибудь умный способ сделать это. Вкратце, это сопоставление домена один-к-одному (4 байта или 256 ^ 4). Да, и такие простые вещи, как N + 1, нельзя использовать, он должен быть связан с массивом 64 * 64 байтовых значений, которые являются случайными sudo, но могут быть воссозданы для обратных преобразований.

В каком линейном пространстве находится это линейное преобразование? Например, поле целых чисел по модулю 2 ** 32 или 32-мерное векторное пространство над целыми числами по модулю 2, что ли?

Darius Bacon 21.01.2009 22:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
1 115
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

Побитовое исключение Или ваш друг.

Если R = A XOR B, R XOR A дает B, а R XOR B возвращает A. Итак, это обратимое преобразование, если вы знаете результат и один из входов.

И не выполняет требование об изменении всех четырех байтов, если затронут только один.

David Thornley 21.01.2009 20:06

Сбалансированные блочные смесители - это именно то, что вы ищете.

Кто знал?

Для двухбитной версии o1 = i1 и o2 = i1 xor i2. По крайней мере, это просто.

David Thornley 21.01.2009 20:25

@Allain Lalonde: Нет, исходная проблема возможна, хотя две группы по 1 биту - нет. (Возможны 2 группы по 2 бита.) @ Дэвид Торнли: o1 не изменяется, если изменяется i2, нарушая "распределительное" свойство OP.

A. Rex 21.01.2009 20:43

Простое доказательство, о котором я только что подумал: если i1 изменяет и o1, и o2, а i2 изменяет и o1, и o2, то изменение i1 и i2 оставляет o1 и o2 одинаковыми, а изменение i1 имеет тот же эффект, что и изменение i2.

David Thornley 21.01.2009 21:20

Что вы подразумеваете под «линейным» преобразованием? O (n) или функция f с f (c * (a + b)) = c * f (a) + c * f (b)?

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

Обновлено: Мое решение было бы таким:

b0 = (a0 ^ a1 ^ a2 ^ a3)
b1 = a1 + b0 ( mod 256)
b2 = a2 + b0 ( mod 256)
b3 = a3 + b0 ( mod 256)

Это было бы обратимым (просто вычтите первый байт из другого, а затем выполните XOR 3 результирующих байта с первым), и изменение одного бита изменит каждый байт (поскольку b0 является результатом всех байтов и влияет на все остальные) .

Re. ваше редактирование: Хорошая идея, но опять же это не работает. f (0,0,0,0) = (0,0,0,0) и f (0,0,0,128) = (128,128,128,0). Обратите внимание, что последний байт не меняется.

A. Rex 21.01.2009 20:51

Предполагая, что я понял, что вы пытаетесь сделать, я думаю, что любой блочный шифр выполнит эту работу. Блочный шифр принимает блок битов (скажем, 128) и обратимо отображает его в другой блок того же размера.

Более того, если вы используете OFB режим, вы можете использовать блочный шифр для генерации бесконечного потока псевдослучайных битов. Выполнение XOR этих битов с вашим потоком битов даст вам преобразование для любой длины данных.

Я собираюсь отбросить идею, которая может сработать, а может и не сработать.

Используйте набор линейных функций по модулю 256 с нечетными простыми коэффициентами.

Например:

b0 = 3 * a0 + 5 * a1 + 7 * a2 + 11 * a3;
b1 = 13 * a0 + 17 * a1 + 19 * a2 + 23 * a3;

Если я правильно помню китайскую теорему об остатках, и я не смотрел на нее годами, топор можно восстановить из bx. Может быть, даже есть быстрый способ сделать это.

Я считаю, что это обратимая трансформация. Это линейно, поскольку af (x) mod 256 = f (ax) и f (x) + f (y) mod 256 = f (x + y). Очевидно, что изменение одного входного байта изменит все выходные байты.

Итак, посмотрите китайскую теорему об остатках и посмотрите, работает ли это.

Это хорошая идея, но не сработает. Все выходные байты b0 и т. д. Четные. Таким образом, вы отображаете меньший диапазон, чем домен, и функция не является однозначной.

A. Rex 21.01.2009 20:44

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

David Thornley 21.01.2009 21:14

Умм, да, прости. Если все входы четные или все нечетные, все выходы будут четными. Еще раз, за ​​исключением того, что на этот раз правильно, у вас больше входов, чем выходов.

A. Rex 21.01.2009 21:30

Вставьте все байты в 32-битное число, а затем выполните shl или shr (сдвиг влево или вправо) на один, два или три. Затем разделите его обратно на байты (можно использовать альтернативную запись). Это переместит биты из каждого байта в соседний байт.

Здесь есть несколько хороших предложений (XOR и т. д.). Я бы посоветовал их объединить.

Это не сработает для входов 0 и 1, поскольку независимо от того, сколько вы сдвигаете, 3 байта всегда будут иметь 0.

Allain Lalonde 21.01.2009 21:02

Вот почему я предлагаю объединить его с XOR или ROT13 или чем-то еще. Думаю, это действительно сводится к твоей цели.

Jim McKeeth 22.01.2009 23:48

Вы можете переназначить биты. Давайте использовать ii для ввода и oo для вывода:

oo[0] = (ii[0] & 0xC0) | (ii[1] & 0x30) | (ii[2] & 0x0C) | (ii[3] | 0x03)
oo[1] = (ii[0] & 0x30) | (ii[1] & 0x0C) | (ii[2] & 0x03) | (ii[3] | 0xC0)
oo[2] = (ii[0] & 0x0C) | (ii[1] & 0x03) | (ii[2] & 0xC0) | (ii[3] | 0x30)
oo[3] = (ii[0] & 0x03) | (ii[1] & 0xC0) | (ii[2] & 0x30) | (ii[3] | 0x0C)

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

Ответ принят как подходящий

Вот ваши требования, как я их понимаю:

  1. Пусть B - это пространство байтов. Вам нужна индивидуальная (а значит, и включенная) функция f: B^4 -> B^4.
  2. Если вы измените какой-либо один входной байт, то изменятся все выходные байты.

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

Хорошо, прежде всего нам нужна функция g: B -> B, которая принимает один байт и возвращает один байт. Эта функция должна иметь два свойства: g (x) обратимо, а x ^ g (x) обратимо. [Примечание: ^ - это оператор XOR.] Подойдет любой такой g, но я определю конкретный позже.

Учитывая такой ag, мы определяем f как f (a, b, c, d) = (a ^ b ^ c ^ d, g (a) ^ b ^ c ^ d, a ^ g (b) ^ c ^ d, а ^ б ^ г (в) ^ г). Проверим ваши требования:

  1. Реверсивный: да. Если мы выполним XOR первых двух выходных байтов, мы получим a ^ g (a), но по второму свойству g мы можем восстановить a. Аналогично для b и c. Мы можем восстановить d после получения a, b и c путем XOR первого байта с (a ^ b ^ c).
  2. Распространение: да. Предположим, что b, c и d фиксированы. Тогда функция принимает вид f (a, b, c, d) = (a ^ const, g (a) ^ const, a ^ const, a ^ const). Если изменяется, то будет и ^ const; аналогично, если a изменится, изменится и g (a), а значит, и g (a) ^ const. (Тот факт, что g (a) изменяется, если a изменяется, является первым свойством g; если бы это не было, то g (x) не было бы обратимым.) То же самое верно для b и c. Для d это еще проще, потому что тогда f (a, b, c, d) = (d ^ const, d ^ const, d ^ const, d ^ const), поэтому, если d изменяется, каждый байт изменяется.

Наконец, построим такую ​​функцию g. Пусть T - пространство двухбитовых значений, а h : T -> T - функция такая, что h (0) = 0, h (1) = 2, h (2) = 3 и h (3) = 1. Эта функция имеет два желаемых свойства g, а именно, h (x) обратимы, как и x ^ h (x). (Для последнего проверьте, что 0 ^ h (0) = 0, 1 ^ h (1) = 3, 2 ^ h (2) = 1 и 3 ^ h (3) = 2.) Итак, наконец, чтобы вычислить g (x), разделить x на четыре группы по два бита и взять h каждой четверти отдельно. Поскольку h удовлетворяет двум желаемым свойствам, и между четвертями нет взаимодействия, то же самое и с g.

Редактировать! Возможно нет, если вы действительно хотите линейное преобразование. Вот математическое решение:

У вас есть четыре байта, a_1, a_2, a_3, a_4, которые мы будем рассматривать как вектор a с 4 компонентами, каждый из которых является числом по модулю 256. Линейное преобразование - это просто матрица M 4x4, элементы которой также являются числами по модулю 256. Вы есть два условия:

  1. Из Ma мы можем вывести a (это означает, что M является матрицей обратимый).
  2. Если a и a' отличаются одной координатой, то Ma и Ma' должны отличаться координатой каждый.

Условие (2) немного сложнее, но вот что оно означает. Поскольку M является линейным преобразованием, мы знаем, что

M(a - a) = Ma - Ma'

Слева, поскольку a и a' отличаются одной координатой, a - a имеет ровно одну ненулевую координату. Справа, поскольку Ma и Ma' должны отличаться по каждой координате, Ma - Ma' должен иметь координату каждый, отличную от нуля.

Таким образом, матрица M должна преобразовать вектор с одной ненулевой координатой в единицу со всеми ненулевыми координатами. Таким образом, нам просто нужно, чтобы каждая запись M была модом 256 неделитель нуля, то есть была нечетной.

Возвращаясь к условию (1), что означает обратимость M? Поскольку мы рассматриваем его по модулю 256, нам просто нужно, чтобы его детерминант был обратимым по модулю 256; то есть его определитель должен быть нечетным.

Итак, вам нужна матрица 4x4 с нечетными элементами по модулю 256, определитель которой нечетный. Но это невозможно! Почему? Определитель вычисляется путем суммирования различных произведений входов. Для матрицы 4х4 их 4! = 24 разных слагаемых, и каждое из них, являющееся произведением записей странный, является нечетным. Но сумма 24 нечетных чисел четна, поэтому определитель такой матрицы должен быть четным!

Ваш анализ, заключающийся в том, что «каждая запись M [должна] быть ненулевой» неверна. Например, если некоторая запись M равна 128, а входной байт четный, на выходе будет 0. Нам действительно нужно, чтобы каждая запись M была странный. Но если каждая запись M нечетная, ее определитель четный; см. @David Thornley выше.

A. Rex 21.01.2009 21:14

Спасибо за изменение. Теперь ваш вывод верен (должна работать любая матрица 4x4, все записи и определитель нечетный). К сожалению, таких матриц нет. Например, если вы используете формулу Лейбница для определителей, у вас есть 24 нечетных числа, что в сумме дает четное число.

A. Rex 21.01.2009 21:35

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

A. Rex 21.01.2009 21:52

Между прочим, все это предполагает, что байты соответствуют числам по модулю 256. Если они соответствуют элементам конечного поля порядка 256, то решение @Allain Lalonde является линейным преобразованием. Разница в том, что F_256 не имеет делителей нуля (это поле и все ...).

A. Rex 22.01.2009 22:00

Другие вопросы по теме