Вопрос, который я получил во время моего последнего интервью:
Разработайте функцию
f
, такую что:f(f(n)) == -n
Где
n
32-разрядное целое число со знаком ; Вы не можете использовать арифметику комплексных чисел.Если вы не можете разработать такую функцию для всего диапазона чисел, разработайте ее для максимально возможного диапазона.
Любые идеи?
Ответы:
Как насчет:
В Python:
Python автоматически переводит целые числа в длинные длины произвольной длины. В других языках наибольшее положительное целое число будет переполнено, поэтому оно будет работать для всех целых чисел, кроме этого.
Чтобы это работало для действительных чисел, вам нужно заменить n в (-1) n на
{ ceiling(n) if n>0; floor(n) if n<0 }
.В C # (работает для любого double, кроме случаев переполнения):
источник
Вы не сказали, какой язык они ожидали ... Вот статичное решение (Хаскелл). Это в основном портит 2 самых значимых бита:
Это намного проще в динамическом языке (Python). Просто проверьте, является ли аргумент числом X и верните лямбду, которая возвращает -X:
источник
class C a b | a->b where { f :: a->b }
;instance C Int (()->Int) where { f=const.negate }
;instance C (()->Int) Int where { f=($()) }
,Вот доказательство того, почему такая функция не может существовать для всех чисел, если она не использует дополнительную информацию (кроме 32 битов типа int):
Мы должны иметь f (0) = 0. (Доказательство: предположим, f (0) = x. Тогда f (x) = f (f (0)) = -0 = 0. Теперь -x = f (f (x) )) = f (0) = x, что означает, что x = 0.)
Далее для любого
x
иy
, допустимf(x) = y
. Мы хотимf(y) = -x
тогда. Иf(f(y)) = -y => f(-x) = -y
. Подведем итог: еслиf(x) = y
, тоf(-x) = -y
иf(y) = -x
, иf(-y) = x
.Итак, нам нужно разделить все целые числа кроме 0 на наборы по 4, но у нас есть нечетное число таких целых чисел; Мало того, что, если мы удалим целое число, которое не имеет положительного аналога, у нас все еще будет 2 (mod4) числа.
Если мы удалим 2 оставшихся максимальных числа (по значению abs), мы можем получить функцию:
Конечно, другой вариант - не соблюдать 0 и получить 2 номера, которые мы удалили в качестве бонуса. (Но это просто глупо, если.)
источник
n = -2147483648
(минимальное значение); Вы не можетеabs(n)
в этом случае, и результат будет неопределенным (или исключением).Благодаря перегрузке в C ++:
источник
Или вы можете злоупотребить препроцессором:
источник
Это верно для всех отрицательных чисел.
Поскольку существует еще одно отрицательное число, чем положительное число для целых чисел, дополняющих двойку,
f(n) = abs(n)
оно действительно для еще одного случая, чемf(n) = n > 0 ? -n : n
решение, которое совпадает сf(n) = -abs(n)
. Получил тебя по одному ...: DОБНОВИТЬ
Нет, это не действительно для одного случая больше, поскольку я только что узнал по комментарию Литба ...
abs(Int.Min)
просто переполнится ...Я тоже думал об использовании информации о моде 2, но пришел к выводу, что она не работает ... до раннего. Если все сделано правильно, это будет работать для всех чисел, за исключением того,
Int.Min
что это будет переполнено.ОБНОВИТЬ
Я поиграл с ним некоторое время, ища хороший трюк с манипуляциями, но я не мог найти хороший однострочный, в то время как решение для мод 2 вписывается в один.
В C # это становится следующим:
Для того, чтобы получить его работу для всех значений, вы должны заменить
Math.Abs()
с(n > 0) ? +n : -n
и включают в расчет в качествеunchecked
блока. Тогда вы дажеInt.Min
сопоставлены с самим собой, как непроверенное отрицание.ОБНОВИТЬ
Вдохновленный другим ответом, я собираюсь объяснить, как работает функция и как ее создать.
Давайте начнем с самого начала. Функция
f
неоднократно применяется к заданному значению,n
давая последовательность значений.Вопрос требует
f(f(n)) = -n
, чтобы это было два последовательных примененияf
отрицания аргумента. Два дальнейших примененияf
- всего четыре - снова сводят на нет аргумент и снова дают результатn
.Теперь существует очевидный цикл длины четыре. Подставляя
x = f(n)
и отмечая, что полученное уравнениеf(f(f(n))) = f(f(x)) = -x
выполнено, получаем следующее.Таким образом, мы получаем цикл длиной четыре с двумя числами и двумя числами с отрицанием. Если вы представляете цикл как прямоугольник, отрицательные значения расположены в противоположных углах.
Одним из многих решений для построения такого цикла является следующее, начиная с п.
Конкретный пример такого цикла есть
+1 => -2 => -1 => +2 => +1
. Мы почти закончили. Отмечая, что построенный цикл содержит нечетное положительное число, его четный преемник и оба числа отрицательные, мы можем легко разделить целые числа на множество таких циклов (2^32
кратных четырем) и найти функцию, которая удовлетворяет условиям.Но у нас проблема с нулем. Цикл должен содержать,
0 => x => 0
потому что ноль отрицается сам по себе. И потому что цикл состояний уже0 => x
это следует0 => x => 0 => x
. Это всего лишь цикл длины два, иx
он превращается в себя после двух применений, а не в-x
. К счастью, есть один случай, который решает проблему. ЕслиX
равен нулю, мы получаем цикл длины один, содержащий только ноль, и мы решили эту проблему, заключив, что ноль является фиксированной точкойf
.Выполнено? Почти. У нас есть
2^32
числа, ноль - фиксированная точка, оставляющая2^32 - 1
числа, и мы должны разбить это число на циклы из четырех чисел. Плохо, что2^32 - 1
не кратно четырем - останется три числа не в любом цикле длины четыре.Я объясню оставшуюся часть решения, используя меньший набор 3-битных подписанных итераторов в диапазоне от
-4
до+3
. Мы сделали с нуля. У нас есть один полный цикл+1 => -2 => -1 => +2 => +1
. Теперь давайте построим цикл, начинающийся с+3
.Проблема, которая возникает, состоит в том, что
+4
не представляется как 3-битное целое число. Мы получили бы+4
отрицая ,-3
чтобы+3
- то , что до сих пор действует 3 разрядное целое число - но добавление одного к+3
(двоичный011
) дает100
бинарной. Он интерпретируется как целое число без знака,+4
но мы должны интерпретировать его как целое число со знаком-4
. Так что на самом деле-4
для этого примера илиInt.MinValue
в общем случае это вторая фиксированная точка целочисленного арифметического отрицания -0
иInt.MinValue
отображаются на себя. Таким образом, цикл на самом деле выглядит следующим образом.Это цикл длины два и дополнительно
+3
входит в цикл через-4
. В результате-4
правильно отображается на себя после двух приложений функции,+3
правильно отображается-3
после двух приложений функции, но-3
ошибочно отображается на себя после двух приложений функции.Итак, мы создали функцию, которая работает для всех целых чисел, кроме одного. Можем ли мы сделать лучше? Нет мы не можем. Почему? Мы должны построить циклы длины четыре и иметь возможность охватить весь диапазон целых чисел до четырех значений. Остальные значения являются две неподвижными точками ,
0
иInt.MinValue
которые должны быть отображены на себя и два произвольных целые числаx
и-x
которые должны быть отображены друг с другом с помощью двух приложений функции.Чтобы отобразить
x
на-x
и наоборот , они должны образовывать четыре цикла , и они должны быть расположены на противоположных углах этого цикла. В следствии0
иInt.MinValue
должны быть на противоположных углах тоже. Это будет правильно картуx
и-x
но поменять местами две фиксированные точки0
иInt.MinValue
после двух применений функции и оставить нас с двух провальных входов. Таким образом, невозможно создать функцию, которая работает для всех значений, но у нас есть такая, которая работает для всех значений, кроме одного, и это лучшее, чего мы можем достичь.источник
Используя комплексные числа, вы можете эффективно разделить задачу отрицания числа на два шага:
Самое замечательное, что вам не нужен какой-либо специальный код обработки. Просто умножая на я делаю работу.
Но вы не можете использовать комплексные числа. Таким образом, вы должны каким-то образом создать свою собственную воображаемую ось, используя часть вашего диапазона данных. Поскольку вам нужно ровно столько мнимых (промежуточных) значений, сколько первоначальных значений, у вас остается только половина диапазона данных.
Я попытался визуализировать это на следующем рисунке, предполагая подписанные 8-битные данные. Вы должны были бы масштабировать это для 32-битных целых чисел. Допустимый диапазон для начального n составляет от -64 до +63. Вот что делает функция для положительного n:
Для отрицательного n функция использует промежуточный диапазон -65 ..- 128.
источник
float
противint
). «Кольцо из 4 элементов», которое описывают многие ответы, требует 4 состояний, которые можно представить в виде 2 измерений, каждое из которых имеет 2 состояния. Проблема с этим ответом является то , что оно требует дополнительного пространства для обработки (только «работает» для -64..63, но потребности -128..127 пространства) и прямо не указывается написана формула!Работает кроме int.MaxValue и int.MinValue
источник
0
в0
и-2147483648
к ,-2147483648
так как эти два числа являются неподвижными точками для оператора отрицания,x => -x
. Для остальных чисел следуйте стрелкам на изображении выше. Как ясно из ответа SurDin и его комментариев, в этом случае будет два числа,2147483647
и-2147483647
не останется ни одной другой пары для обмена.Вопрос ничего не знает о том , что тип входного и возвращаемого значения функции не говорят ,
f
должны быть (по крайней мере , не так , как вы представили его) ...... только то, что когда n является 32-разрядным целым числом, то
f(f(n)) = -n
Итак, как насчет чего-то вроде
Если n - 32-разрядное целое число, то утверждение
f(f(n)) == -n
будет истинным.Очевидно, что этот подход может быть расширен для работы с еще более широким диапазоном чисел ...
источник
для javascript (или других динамически типизированных языков) вы можете заставить функцию принимать либо int, либо объект и возвращать другой. т.е.
дающий
В качестве альтернативы вы можете использовать перегрузку в строго типизированном языке, хотя это может нарушить правила, т.е.
источник
В зависимости от вашей платформы, некоторые языки позволяют вам сохранять состояние в функции. VB.Net, например:
IIRC, C ++ также допустили это. Я подозреваю, что они ищут другое решение, хотя.
Другая идея состоит в том, что, поскольку они не определили результат первого вызова функции, вы можете использовать нечетность / четность, чтобы контролировать, следует ли инвертировать знак:
Добавьте одно к величине всех четных чисел, вычтите одно из величины всех нечетных чисел. Результат двух вызовов имеет одинаковую величину, но один вызов, даже если мы меняем знак. В некоторых случаях это не сработает (-1, max или min int), но работает намного лучше, чем все, что предлагалось до сих пор.
источник
Использование исключений JavaScript.
источник
Для всех 32-битных значений (с оговоркой, что -0 равен -2147483648)
В основном вам нужно соединить каждый цикл -x => x => -x с циклом ay => -y => y. Так что я в паре противоположных сторон
split
.Например, для 4-битных целых чисел:
источник
Версия C ++, возможно, несколько отклоняющая правила, но работающая для всех числовых типов (с плавающей точкой, целых, двойных) и даже для типов классов, которые перегружают унарный минус:
источник
x86 asm (стиль AT & T):
Код проверен, все возможные 32-битные целые числа переданы, ошибка с -2147483647 (недополнение).
источник
Использует глобалы ... но так?
источник
Это решение Perl работает для целых чисел, чисел с плавающей точкой и строк .
Попробуйте некоторые тестовые данные.
Вывод:
источник
n
была строка, которую я мог бы сделать, 548 становится «First_Time_548», а затем в следующий раз, когда он проходит через функцию ... if (prefix == First_Time_ ") замените« First_Time_ »на« - »Никто никогда не говорил, что f (x) должен быть того же типа.
источник
На самом деле я не пытаюсь дать решение самой проблемы, но у меня есть пара комментариев, поскольку в вопросе говорится, что эта проблема была поставлена как часть интервью (работа?):
int.MinValue
вint.MaxValue
, и для каждогоn
в этом вызове диапазонаf(f(n))
и проверяя результат-n
), сказав, что затем я буду использовать Test Driven Development, чтобы добраться до такой функции.О, этот ответ предполагает, что интервью было для позиции, связанной с программированием на C #. Конечно, было бы глупым ответом, если бы собеседование проходило по математической позиции. ;-)
источник
Я бы вам изменил 2 самых значимых бита.
Как вы можете видеть, это всего лишь дополнение, исключающее переносимый бит.
Как я дошел до ответа? Моей первой мыслью была просто необходимость симметрии. 4 поворота, чтобы вернуться туда, где я начал. Сначала я подумал, что это 2-битный код Грея. Тогда я подумал, что на самом деле стандартного двоичного файла достаточно.
источник
Вот решение, основанное на требовании или утверждении, что комплексные числа не могут быть использованы для решения этой проблемы.
Умножение на квадратный корень из -1 является идеей, которая, похоже, не удалась, потому что -1 не имеет квадратного корня над целыми числами. Но игра с такой программой, как mathematica, дает, например, уравнение
и это почти так же хорошо, как квадратный корень из -1. Результатом функции должно быть целое число со знаком. Следовательно, я собираюсь использовать модифицированные моды по модулю (x, n), которые возвращают целое число y, совпадающее с x по модулю n, которое ближе всего к 0. Только очень немногие языки программирования имеют операцию по модулю, но ее легко определить , Например, в Python это:
Используя приведенное выше уравнение, проблема теперь может быть решена как
Это удовлетворяет
f(f(x)) = -x
всем целым числам в диапазоне . Результаты также находятся в этом диапазоне, но, конечно, для вычислений потребуются 64-битные целые числа.[-2
31
-2, 2
31
-2]
f(x)
источник
C # для диапазона от 2 ^ 32 до 1 числа, все числа int32, кроме (Int32.MinValue)
печатает:
источник
По сути, функция должна делить доступный диапазон на циклы размера 4 с -n на противоположном конце цикла n. Тем не менее, 0 должен быть частью цикла размера 1, потому что в противном случае
0->x->0->x != -x
. Поскольку 0 один, в нашем диапазоне должно быть 3 других значения (размер которых кратен 4), которые не находятся в правильном цикле с 4 элементами.Я выбрал эти дополнительные странные значения
MIN_INT
,MAX_INT
иMIN_INT+1
. Кроме того,MIN_INT+1
будет отображатьсяMAX_INT
правильно, но застрять там, а не обратно. Я думаю, что это лучший компромисс, потому что он обладает хорошим свойством только экстремальных значений, которые не работают правильно. Кроме того, это означает, что это будет работать для всех BigInts.источник
Никто не сказал, что это должно быть без гражданства.
Обман, но не так много примеров. Еще большим злом было бы заглянуть в стек, чтобы увидеть, является ли адрес вашего вызывающего абонента & f, но это будет более переносимым (хотя и не поточно-безопасным ... поточно-ориентированная версия будет использовать TLS). Еще больше зла
Конечно, ни один из них не работает слишком хорошо для случая MIN_INT32, но вы мало что можете с этим поделать, если вам не разрешено возвращать более широкий тип.
источник
Я мог бы представить, что использование 31-го бита в качестве мнимого ( i ) бита было бы подходом, который поддерживал бы половину всего диапазона.
источник
работает для n = [0 .. 2 ^ 31-1]
источник
Задача гласит «32-битные целые числа со знаком», но не указывает, являются ли они двойным или единичным дополнением .
Если вы используете дополнение с единичным значением, то все значения 2 ^ 32 встречаются в циклах длины четыре - вам не нужен специальный случай для нуля, а также вам не нужны условные выражения.
В С:
Это работает
После двух проходов мы получаем побитовую инверсию исходного значения. Который в однополном представлении эквивалентен отрицанию.
Примеры:
источник
: D
источник
источник
Я хотел бы поделиться своей точкой зрения на эту интересную проблему как математик. Я думаю, что у меня есть самое эффективное решение.
Если я правильно помню, вы отрицаете 32-разрядное целое число со знаком, просто переворачивая первый бит. Например, если n = 1001 1101 1110 1011 1110 0000 1110 1010, то -n = 0001 1101 1110 1011 1110 0000 1110 1010.
Итак, как нам определить функцию f, которая принимает 32-разрядное целое число со знаком и возвращает другое 32-разрядное целое число со знаком со свойством, что двойное получение f равнозначно переключению первого бита?
Позвольте мне перефразировать вопрос, не упоминая арифметические понятия, такие как целые числа.
Как мы определяем функцию f, которая принимает последовательность нулей и единиц длины 32 и возвращает последовательность нулей и единиц одинаковой длины со свойством, что двойное взятие f совпадает с переключением первого бита?
Замечание: если вы можете ответить на поставленный выше вопрос для 32-битного регистра, то вы также можете ответить для 64-битного регистра, 100-битного регистра и т. Д. Вы просто применяете f к первым 32-битным.
Теперь, если вы можете ответить на вопрос для 2-битного случая, вуаля!
И да, оказывается, что изменение первых двух бит достаточно.
Вот псевдокод
Примечание. Шаг 2 и шаг 3 вместе можно разделить на лету как (a, b) -> (-b, a). Выглядит знакомо? Это должно напомнить вам о повороте плоскости на 90 градусов и умножении на квадратный корень из -1.
Если бы я представил только один псевдокод без длинной прелюдии, это казалось бы кроликом из головы, я хотел бы объяснить, как я получил решение.
источник