2013-08-23 2 views
4

Легко «переключатель» между 0 и 1 следующим образом:Как я могу упростить свое «уравнение» для переключения между 3 и 5?

int i = 0; 
i = (++i) % 2; // i = 1 
i = (++i) % 2; // i = 0 

Кроме того, я обнаружил, что можно «переключатель» между 3 и 5:

int i = 3; 
i = (((i * 2) - 1) % 3) + 3; // i = 5 
i = (((i * 2) - 1) % 3) + 3; // i = 3 

В то время как это кажется громоздким, я ищу более сжатый способ сделать это. Можно ли это упростить? Если да, то как? Кстати, я использую это для чего-то.

+0

huh, почему вы не можете сделать 'i = 3' и' i = 5', вы действительно должны дать этому коду некоторый контекст – aaronman

+5

Вы можете переключаться между 0 и 1 с помощью 'i =! I;' (но не 'i! = i;'), а также 'i = 1 - i;'. Для переключения между 3 и 5 вы можете использовать 'i = 8 - i;'. –

ответ

12

намного короче:

int i = 3; 
i = 8 - i; 
i = 8 - i; 

И, конечно же, для 0/1 переключения, вы должны сделать это:

int i = 0; 
i = 1 - i; 
i = 1 - i; 

И в общий, для a/b переключатель, сделать это:

int i = a; 
i = (a + b) - i; 
i = (a + b) - i; 

Как это работает? Ну, a + b - a - b, и a + b - b - a. :-D

+0

(Пока 'a + b' не переполняется, так или иначе) –

+0

@Stephen Смотрите мой комментарий к roliu (в сообщении ниже). –

+1

Хотя я согласен с тем, что это работает на большинстве распространенных систем, платформы, которые насыщаются при переполнении, существуют, что нарушает эту идиому; это стоит знать об этом. –

3

Вы могли бы сказать:

i = 3; 
i = (i == 5) ? 3 : 5; // it's five now 
i = (i == 5) ? 3 : 5; // it's three now 
i = (i == 5) ? 3 : 5; // it's five again 
+1

Но но ... ветвление так медленно! > _ < –

+0

@ ChrisJester-Young: Ничто об этом решении не подразумевает ветвление. Компилятор мог бы использовать условные ходы, или он мог бы скомпилировать это с той же арифметикой, что и ваше решение, или (более вероятно) он будет просто постоянно распространять всю вещь, эквивалентную просто «i = 5». Вы не можете осмысленно говорить о высказываниях на языке C как о «ветвлении» или «медленном». –

+0

@ StephenCanon I 100% согласен. Мой комментарий был увлекательным, хотя я полагаю, что не все это будут хорошо читать. (Серьезный комментарий: я обычно не допускаю, чтобы микрооптимизация определяла, как я пишу код, если профилирование не указывает на необходимость.) –

9

Другой способ заключается в использовании XOR, потому что a^(a^b) == b и b^(a^b) == a:

int i = 3; 
i ^= 3^5; // i == 5 
i ^= 3^5; // i == 3 
+2

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

+1

@roliu Я согласен, хотя в системах с двумя дополнениями, как правило, переполнения и недоиспользования не ушибают, хотя технически это считается неопределенным поведением. –

+0

Это правда. Я не должен бездумно говорить, что переполнение/недополнение плохо, поскольку добавление будет работать, вероятно, в 100% случаев здесь, в стандартных реализациях C. Но хорошо знать, что это также не определено – rliu

0

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

#include <stdio.h> 

int mapModIntToSequence (int i, int mod, int x[]) { 
    return x[i%mod]; 
} 

int main() { 
    int i; 
    int x[] = {2,7}; 
    for (i = 0; i < 10; i++) { 
     printf ("%d\n",mapModIntToSequence(i,2,x)); 
    } 
} 

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

+0

Цель состоит в том, чтобы выполнить 'i = mapModIntToSequence (i, 2, x);' так что 'i' содержит новое значение. К сожалению, ваш код не справляется с этим. Вы кодируете код последовательности для элемента цикла; он не отображает элемент цикла в следующий элемент цикла. –

1

также намного короче:

i = 3; 
i ^= 6; // now i = 5 
i ^= 6; // now i = 3 

Для переключения между двумя числами a и b, необходимо постоянное значение a XOR b - что 1 для первого примера и 6 в вашей второй.