Как мне найти следующий бит, чтобы изменить код Грея за постоянное время? - PullRequest
12 голосов
/ 10 января 2011

У меня небольшой 8-битный процессор, в некоторых выходных линиях которого есть декодер N-M-M - например, для случая 5–32 бит я записываю 00101, а бит 5 изменяет состояние.Единственный интерфейс к выходу - состояние изменения, нет никакого обратного чтения.

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

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

Однако я хочу иметь возможность отслеживать следующий бит для эффективного изменения.

У меня нет инструкции "LowestBitSet", и поиск младшего бита, установленного для четырех 8-битных регистров, занимает много времени - поэтому я не могу использовать этот "общий" подход:

  Keep binary counter A
  Find B as A XOR (A+1)
  Bit to change is LowestBitSet in B 

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

Есть предложения по алгоритмам?

Ответы [ 7 ]

1 голос
/ 11 января 2011

Вам не нужно вычислять коды Грея и делать их xor, вы можете просто использовать сам счетчик, а затем использовать таблицу поиска из 256 элементов для подсчета числа конечных нулей.Например:

unsigned char bit_change(unsigned char counter[4]) {
  static const unsigned char ones[] = {
    0,0,0,1,0,1,1,2,0,1,1,2,1,2,2,3,0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
    0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
    0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
    0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
    2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,    
  };

  unsigned char i;
  for (i = 0; i < 4; i++) {
    unsigned char x = counter[i];
    if (x) {
      x ^= x - 1;
      return 8 * i + ones[x];
    }
  }
}

Если развернуть цикл, это будет максимум 2 добавления, 1 xors и 5 загрузок (но почти всегда меньше).Если у вас нет 256 байт для таблицы, вы можете использовать ту же стратегию для клевов.

0 голосов
/ 20 марта 2017

/ *
Как ранее опубликовал этот ответ, https://stackoverflow.com/questions/4648716#42865348 с помощью счетчика Джонсона кода Грея очень просто:

Number_of_Bit_To_Flip = ++Number_of_Bit_To_Flip % Total_Number_of_Counter_Bits

, который выполняется при каждом возникновении события.

В противном случае, используя двоичный код с отраженным серым и 4-байтовый счетчик с основанием 2 n, ...

Метод 1 - с использованием таблицы * /

static const byte twos[ ] = { //  note pattern    V       V       V V V V
  0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,    7,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      8,
};

//  operation count worst case    3 logic   4 index     1 addition
//            for 7/8 of calls    2         3           1

byte bit_change(byte ctr[4]) {
 return  
  (byte[]){ 
    (byte[]){  16 + twos[ctr[2]]               ,
      (byte[]){24 + twos[ctr[3]] ,
               31                } [ !ctr[3] ] } [ !ctr[2] ] ,
    (byte[]){   0 + twos[ctr[0]]               ,
                8 + twos[ctr[1]]               } [ !ctr[0] ] }
                                                         [ctr[0] || ctr[1]];

// --------  NB. orphaned, included for pedagogic purposes  --------

  return  (byte[]){ 0 + twos[ctr[0]] ,   //    this IS totally time constant
                    8 + twos[ctr[1]] ,   // NB. 31 + time "constantator" 
                   16 + twos[ctr[2]] ,   // case ctr[i]==0 for all i
                   24 + twos[ctr[3]] ,
                   31 + twos[ctr[0]] } [ !ctr[0]*( 1+ 
                                         !ctr[1]*( 1+
                                         !ctr[2]*( 1+
                                         !ctr[3]       ) ) ) ];  
 }

/ Метод 2 - без таблиц * /

byte bin2toN(byte b){  
   return 
      (byte []) {(byte []) {(byte []) {7,6} [b < 128 ] , 
                            (byte []) {5,4} [b <  32 ] } [b < 64 ] ,
                 (byte []) {(byte []) {3,2} [b <   8 ] , 
                            (byte []) {1,0} [b <   2 ] } [b <  4 ] } [b < 16 ] ;
} 

byte flip_bit(byte n[4]){  
return    
  (byte []){ 
    (byte []){   16 + bin2toN(  n[2] & -n[2] )            ,
      (byte []){ 24 + bin2toN(  n[3] & -n[3] ),
                 31                           } [ !n[3] ] } [ !n[2] ] ,
    (byte []){    0 + bin2toN(  n[0] & -n[0] )            ,
                  8 + bin2toN(  n[1] & -n[1] )            } [ !n[0] ] } 
                                                               [ n[0] || n[1] ] ;

 // - - - - - - - - - - - - ORPHANED, fails on zero - - - - - - - - - - - -

  return                             //  included for pedagogic purposes
    (byte []) {                         
      (byte []) { bin2toN(  n[2] & -n[2] ) + 16 ,
                  bin2toN(  n[3] & -n[3] ) + 24 } [ !n[2] ] ,
      (byte []) { bin2toN(  n[0] & -n[0] ) +  0 ,
                  bin2toN(  n[1] & -n[1] ) +  8 } [ !n[0] ] } [ n[0] || n[1] ] ;
}

/ *
Битовые кролики и кодеры Cargo Cult не нужно читать дальше.

Эффективность выполнения вышеуказанного кода зависит от того, что n[0], n[1], ... вычисляются во время компиляции как фиксированные адреса, что довольно условно. Кроме того, использование оптимизирующего компилятора по требованию ускорит содержимое массива поэтому необходимо рассчитать только одно индексированное значение. Эта сложность компилятора, вероятно, отсутствует, но ее легко собрать вручную необработанный машинный код для этого (в основном, переключатель, вычисленное goto и т. д.).

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

В обоих методах не осиротевшие возвраты требуют обработки случая, когда счетчик переворачивается на 0, следовательно, используя дополнительный индекс и логический (!) операции. Это происходит за 1/2 от 1/2 от 1/2 или 1/8 от общего количества, и на один счет в этой 1/8 ничего не остается, кроме как вернуть 31.

Первый метод требует 2 логических операций (! ||), 1 сложение и 3 индекса расчеты для 7/8 от общего количества. На один отсчет ноль, 3 логических и Требуются 3 операции с индексами, а остальным 1/8 - 3 логических, 1 сложение и 4 операции с индексами.

Окончательный код выполнения метода 2 (скомпилирован оптимально) для 7/8 из вычисления, использует 7 логических операций (|| & ! < - последний является дополнением до двух), 1 арифметическая (+) и 5 ​​вычисляемых операций с индексами. Другой 1/8, но один Например, нужно 8 логических, 1 сложение и 6 вычисляемых операций с индексами.

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

Как это было сделано, включая решающее предварительное расследование, как задокументировано: https://stackoverflow.com/a/42846062. Затем код был получен с использованием последовательного процесса уточнения, начинающегося с оценки алгоритмов в этом посте.
Конкретно этот ответ: https://stackoverflow.com/a/4657711.

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

* /

byte bit_change(byte ctr[4]) {
  static byte ones[256]; //  this sparse RA is precomputed once
    for (byte i = 255; --i; ones[i]=0) ;
    ones[ 0] =    ones[ 1] = 0; ones[ 3] = 1; ones[  7] = 2; 
    ones[15] = 3; ones[31] = 4; ones[63] = 5; ones[127] = 6; ones[255] = 7;

//   { ie. this very sparse array is completely adequate for original code
//    0,0, ,1, , , ,2, , , , , , , ,3, , , , , , , , , , , , , , , ,4,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,5,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,6,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,7,  }

//  1/2 of count uses 2 index 2 conditionals 0 increment 1 logic 2 +/-  1 x
//  1/4               3       4              1           1       2      1
//  1/8               4       6              2           1       2      1
//  1/16              5       8              3           1       2      1
//     average  14 = 3.5      5             1.5          1       2      1  

unsigned char i;  for (i = 0; i < 4; i++) {         //  original code
                    unsigned char x = counter[i];   //  "works" but
                         if (x) {                   //  still fails on 
                           x ^= x - 1;              //  count 0 rollover
                           return 8 * i + ones[x];
                   }     }

//  .............................  refinement .............................
byte x;             for (byte i = 0; i < 4; i++)         //
                         if (x = counter[i]) 
                              return  i<<3 + ones[x ^ x - 1];
}

/ ------------------------------------------- ------------------- -------------------------------- /

//              for (byte i = 255; --i; twos[i] == ones[i ^ i-1] ) ;
// ones[ ] uses only 9 of 1/4K inefficient,  twos[ ] uses all 1/4K

static const byte twos[ ] = {  
 0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,    7,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      8,
};


//  fix count 0 rollover failure, make time absolutely constant as per OP

byte f0(byte ctr[4]) {
  byte ansr=31;
  for (byte i = 0; i < 4; i++) 
   if (ctr[i]) {
       ansr=(byte[]){0,8,16,24}[i] + twos[ctr[i]];    //  i<<3 faster?
       break;
    }
  for (; i < 4; i++) if (ctr[i]) ;
  return ansr;
}

//..................................................

// loop ops (average):  1.5 ++   2.5 []  5 if
// result calculation:   1  +     2  []       significant loop overhead

byte f1(byte counter[4]) {
  for (byte i = 0; i < 4; i++) 
    if (counter[i]) 
      return  (byte[]){  0 + twos[counter[0]], 
                         8 + twos[counter[1]],                           
                        16 + twos[counter[2]], 
                        24 + twos[counter[3]]  } [i];
  return 31;
}

//..................................................

//    5 +/++    6 []     10 if

byte f2(byte counter[4]){  
  byte i, ansr=31;
  for (i = 0; i < 4; i++) {      //  definite loop overhead
    if (counter[i]) {
       ansr= (byte[]){   0 + twos[counter[0]], 
                         8 + twos[counter[1]],                           
                        16 + twos[counter[2]], 
                        24 + twos[counter[3]]  } [i];
       break;
  } }
  for (; i < 4; i++)   if (counter[i]);  //   time "constantator"
  return ansr;
}

//..................................................

//   4 +    4 !    3 x    1 []    1 computed goto/switch

byte f3(byte counter[4]){          // default: time "constantator"
  switch (!counter[0]*( 1 +        //           counter[0]==0 !!
          !counter[1]*( 1 +
          !counter[2]*( 1 +
          !counter[3]        ) ) ) ){
              case 0:  return   0 +  twos[ counter[0] ] ;
              case 1:  return   8 +  twos[ counter[1] ] ;
              case 2:  return  16 +  twos[ counter[2] ] ;
              case 3:  return  24 +  twos[ counter[3] ] ;
              default: return  31 +  twos[ counter[0] ] ;
}         }

/ *

Существует сопоставимая хронология для метода 2.

Эта последовательность была радикально ослаблена и сокращена до промежуточного примера:

Случайно код, размещенный в https://stackoverflow.com/a/42865348, был не только в байтовом размере, как задумано. Предполагаемый код в этом посте. * /

byte log2toN(byte b){ return    ( b & 0xF0 ? 4:0 )  +   //    4444....
                                ( b & 0xCC ? 2:0 )  +   //    22..22..
                                ( b & 0xAA ? 1:0 )  ;   //    1.1.1.1.
} 
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
byte BRGC(byte n){ return n ^ n>>1; }

byte bitNbyte(byte n){ return log2toN( BRGC(n) ^ BRGC(n+1) ); }

byte bit2flip(byte n[4]){  
   boolean n3=n[3]<255, n2=n[2]<255, n1=n[1]<255, n0=n[0]<255;
           return n0*( 0 + bitNbyte( n[0] ) ) + !n0*( 
                  n1*( 8 + bitNbyte( n[1] ) ) + !n1*( 
                  n2*(16 + bitNbyte( n[2] ) ) + !n2*( 
                  n3*(24 + bitNbyte( n[3] ) ) + !n3*( 0 ) ) ) );
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
byte bit_flip(byte n[4]){  
//switch(     ( !!n[0] << 3 ) | ( !!n[1] << 2 ) | ( !!n[2] << 1 )  |  !!n[3] )
  switch( 15 ^ ( !n[0] << 3 ) ^  ( !n[1] << 2 ) ^  ( !n[2] << 1 )  ^   !n[3] ) { 
      case 15: case 14: case 13: case 12:
      case 11: case 10: case  9: case  8:  return  0 + log2toN(  n[0] & -n[0]  );
      case  7: case  6: case  5: case  4:  return  8 + log2toN(  n[1] & -n[1]  );
                        case  3: case  2:  return 16 + log2toN(  n[2] & -n[2]  );
                                 case  1:  return 24 + log2toN(  n[3] & -n[3]  );
      default:                             return 31 + log2toN(  n[0] & -n[0]  );
}           }

/ *
Риторически, ответ на Как мне найти ... можно ответить только в явном виде в личном смысле (см. этот ответ: https://stackoverflow.com/a/42846062) как невозможно говорить за когнитивные способности других людей.

Содержание https://stackoverflow.com/a/42846062 имеет решающее значение для фона информация и отражает очень личное педантичный механизм, необходимый для умственной гимнастики, чтобы решить эту проблему. Несомненно, обстановка и изобилие материала устрашают, но это точно индивидуальный подход к получению достаточного понимания, репертуар, анекдотические прецеденты и т.д., чтобы экстраполировать и интерполировать ответ, чтобы ответить конкретно, какая программа соответствует критериям, в точности . Кроме того, именно эта «погоня» возбуждает и мотивирует (возможно, патологическую) психику тратить время и силы утолить любопытство любознательным квестом. * /

void setup() {    }

void loop() {     }
0 голосов
/ 17 марта 2017

Алгоритм, представленный OP, не генерирует код Грея.

Алгоритм в этом ответе: https://stackoverflow.com/a/4657711/7728918 равен , а не постоянному времени, поскольку условный тест if (x) может варьироваться от 1 до 4 выполнений в зависимости от значения * 1008.*.Это изменяет количество времени, необходимое для вычисления битовых чисел.Будет 4 различных возможных времени выполнения, которые могут иметь любые вычисления.

См. (За исключением кодировщиков культа грузов) ссылку на обоснование следующего, который соответствует этому постоянному требованию времени (без огней, без машины, ни одной роскоши ... даже не"таблица"):

byte log2toN(byte b){ 
   return 7 - (byte) ( 0x10310200A0018380uLL >> ( (byte)(0x1D*b) >>2 ) ) ; }  
byte BRGC(byte n){ return n ^ n>>1; }
byte bit2flip(byte n){ return log2toN( BRGC(n) ^ BRGC(n+1) ); }

Однако существует гораздо лучший, более лаконичный и целесообразный метод, отвечающий критериям ФП.

Для целей кодирования культа грузов следующее удобно удовлетворяет условиям ОП минимально (максимально?;).

Номер бита, который нужно изменить, находится каждый раз только с помощью двух операций: модуль (который при выполнении по модулю 2^n может быть таким же простым, как побитовая & операция сn-1 бит, т. Е. Константа 2^n - 1) и приращение.

Фактическая последовательность кода Джонсона Грея (JGC) генерируется постепенно с помощью XOR с использованием предыдущего кода с желаемым битом, выбранным смещением влево на 1 в положение номера бита.Этот расчет НЕ необходим в соответствии с требованиями ОП.

Код Джонсона
-------------------------

фактическое кодирование Грея не имеет значения, поэтому использование кода Грея для счетчика Джонсона исключительно тривиально.

Обратите внимание, что плотность кода Джонсона Грея (JGC) является линейной, а не логарифмической, как основа 2 или двоичный код Грея (BRGC).

С 32 битами в 4 байта последовательность может считать от 0 до 63 перед сбросом.

/ . / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 1045 *

byte  bitCtr=-1;               //   for 4 x 8 bits use 32 instead of 5
int JohnsonCode(){ static byte GCbits = 0; 
                       return  GCbits ^=  1u << ( bitCtr = ++bitCtr %5 ); }

/ . // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / // / / / / / / / / / / / / / / / / / / .

Тестовый вывод:

  Johnson counter   Bit
     Gray code:   Flipped:
       ....1         0
       ...11         1
       ..111         2
       .1111         3
       11111         4
       1111.         0
       111..         1
       11...         2
       1....         3
       .....         4
       ....1         0
       ...11         1   etc.   

, частично сгенерированный с этим кодом:

void testJohnson(){           
   Serial.println("\n\tJohnson counter\t   Bit\n\t   Gray code:\t Flipped:");

   for( int intifiny=31; --intifiny;  )
      Serial.println( "\t     " + cut( 5, "....." +  

// s.replace(...) returns zip, 
//                    so VVV use lambda function      invoke via VVV      
//  _____________________ V ____________________   ______________ V _____________ 
   [](String  s){ s.replace("0","."); return s; } ( String( JohnsonCode(), BIN ) )

         ) + "\t    " + bitCtr
      );
}  

/ *

К сожалению , этот ответ не объясняет "Как вы (я, я, ...) нашли ...".

Подробнее о методологии нахождения таких решений и использовании BRGC аналогичным образом ...
см. Предыдущий документ: https://stackoverflow.com/a/42846062/7728918

0 голосов
/ 17 марта 2017

/ *
Невозможно отредактировать предыдущий ответ согласно комментарию, поэтому переписывание сообщения:

Слишком нетерпелив?
Для немедленного удовлетворения и минимального назидания, перейдите к преследованию и преследуйте эту ссылку, где был опубликован только окончательный результат:
C-код для генерации следующего бита, чтобы перевернуть серый код

РЭС:
C-код для генерации следующего бита, чтобы перевернуть серый код
Как найти следующий бит для изменения кода Грея за постоянное время?

Получение n-го кода Грея из (n-1) -го кода Грея
Функция увеличения кода Грея
Эффективный способ перебора позиций изменения кода Грея
Генерация серых кодов.

https://en.wikipedia.org/wiki/The_C_Programming_Language  
https://en.wikipedia.org/wiki/The_Elements_of_Programming_Style  

ВНИМАНИЕ:
В целях целесообразности кодирования и наглядного функционального исполнения, были использованы некоторые нетривиальные методы программирования. Тем не менее, это Надеемся, что смягчены для изложения зачатков концепции представляя суть как можно более простым и минимальным выделение / / / /. Внимательное чтение, изучение и эксперименты избегайте кодирования грузов, недосмотра и ошибок.

Этот ответ проявляется в среде кодирования ядра Arduino IDE ESP8266.

Алгоритм, предложенный ОП, не совсем корректен (как если бы это;).

Код Джонсона
-------------------------

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

Обратите внимание, что плотность кода Грея для счетчика Джонсона является линейной, а не логарифмической.
С 32 битами в 4 байтах последовательность может считать от 0 до 63 перед сбросом.

Необходимо тщательно проверить функциональную пригодность следующего кода, модифицируя его соответствующим образом.

СОВЕТ: Проверка ОБЯЗАНА, особенно для "двоичного отраженного" кода Грея (BRGC)!

/ . / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 10/10 byte bitCtr=-1; // for 4 x 8 bits use 32 instead of 5 byte JohnsonCode(){ static byte GCbits = 0; return GCbits ^= 1u << ( bitCtr = ++bitCtr %5 ); } / . / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 10 / void testJohnson(){ Serial.println("\n\tJohnson counter\t Bit\n\t Gray code:\t Flipped:"); for( int intifiny=31; --intifiny; ) Serial.println( "\t " + cut( 5, "....." + // s.replace(...) returns zip, // so VVV use lambda function invoke via VVV // _____________________ V ____________________ ______________ V _____________ [](String s){ s.replace("0","."); return s; } ( String( JohnsonCode(), BIN ) ) ) + "\t " + bitCtr ); } / *
Выходы:

  Johnson counter   Bit
     Gray code:   Flipped:
       ....1         0
       ...11         1
       ..111         2
       .1111         3
       11111         4
       1111.         0
       111..         1
       11...         2
       1....         3
       .....         4
       ....1         0
       ...11         1   etc.   

Некоторые справочные материалы по двоичному коду с отраженным серым (BRGC)
----------------------------------------------- ------------------------------------------------
ПРЕВРАЩЕНИЯ:
---------------------
REF: Код Гольф: Серый код

// These javascript scURIples may run by copy and paste to the URI browser bar.

// convert base 2  to  BRGC:   n^=n>>1  
//     get base 2 from BRGC:   n^=n>>1   n^=n>>2   n^=n>>4  ...

javascript:      n=16; s="<pre>";  
                      function f(i,n){ return i?f(i>>1,n^n>>i):n}
                                  while(n--) s +=  f(4,n^n>>1) .toString(2)+"\n";

javascript:      n=16; s="<pre>"; while(n--) s +=     (n^n>>1) .toString(2)+"\n";

javascript: c=0; n=16; s="<pre>"; while(n--) s +=(c^(c=n^n>>1)).toString(2)+"\n";

СЧЕТ:
-----------------
Следующее (как указано выше) произвольно получает как предыдущий, так и следующий BRGC для кода.
NB! Порядок n1 и n2 определяется по четности и не соответствует в противном случае. Порядок может быть n1, gray, n2 ИЛИ может быть n2, gray, n1, поэтому eo (четность) различает.

unsigned n1 = gray ^ 1;
unsigned n2 = gray ^ ((gray & -gray) << 1); 
gray = eo=!eo ? n1 : n2;                   // eo (or parity) gets Every Other  

т.

bitToFlip =  eo=!eo ? 1 : (gray & -gray) << 1;   gray ^= bitToFlip;  

отсюда

    gray ^=  eo=!eo ? 1 : (gray & -gray) << 1;    

/ . / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 110 / * / 1103 * byte tooGray( byte (*f)(byte) ){ static byte BRGC=0, base2=0; static boolean eo=false; return (*f)( BRGC ^= (eo=!eo) ? (BRGC & -BRGC) <<1 : 1 ) & 0x3 | // count ^---------------------------------------^ in raw BRGC (*f)( base2 ^ base2++ >>1 ) & 0xC ; } // count in base 2 ^---------------------^ and convert to BRGC / . / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / * / 110 /

REF: Соседи в коде Грея

http://www.graphics.stanford.edu/~seander/bithacks.html   
http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html  
https://en.wikipedia.org/wiki/Ring_counter#Johnson_counter   

Ах да, ... посчитайте установленные биты в A ^ A+1, которые будут иметь битовую комбинацию, такую ​​как 000 ... 0111..1 Доказательство.

Как получить битовую позицию для степени 2 - параметры n должны иметь один бит.

Метод 1
* /

byte naive1(byte n){ return  bitNumber(n-1);   }  

byte bitNumber(byte m){  // can use A ^ A+1  ...  BUT >> 1 first OR -1 after
 return ( m & 1  ?1:0 ) + ( m &  2 ?1:0 ) + ( m &  4 ?1:0 ) + ( m &   8 ?1:0 ) +
        ( m & 16 ?1:0 ) + ( m & 32 ?1:0 ) + ( m & 64 ?1:0 ) + ( m & 128 ?1:0 );}
       //    256               512              1024               2048  ...

/ *
Метод 2
* /

byte naively2(byte n){
 return ( n & 1  ?0:0 ) + ( n &  2 ?1:0 ) + ( n &  4 ?2:0 ) + ( n &   8 ?3:0 ) +
        ( n & 16 ?4:0 ) + ( n & 32 ?5:0 ) + ( n & 64 ?6:0 ) + ( n & 128 ?7:0 );}

/ *
Метод 3
* /

byte powerOf2(byte n){ return ( n & 0xF0 ? 4:0 )  +   //    4444....
                              ( n & 0xCC ? 2:0 )  +   //    22..22..
                              ( n & 0xAA ? 1:0 )  ; } //    1.1.1.1.

/ *
Метод 4
* /

// and the penultimate,
//    http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn

byte fastNof2up(byte b){ return (byte []){0,1,6,2,7,5,4,3}[(byte)(b*0x1Du)>>5];}
//                                        7,0,5,1,6,4,3,2           0x3Au
//              b==2^N                    0,1,2,4,7,3,6,5           0x17u
//                                        7,0,1,3,6,2,5,4           0x2Eu
//     |------|                 |------|        
//     .....00011101         ........1101....     
//    ......0011101.        .........101.....     
//   .......011101..       ..........01......   
//  ........11101...      ...........1.......       
//     |------|                 |------|

/ *
Метод 5
Подробности в конце.
Может ли разумный выбор констант уменьшить это до 2 операций?
* /

byte log2toN(byte b){ return 7 - (byte) ( 0x10310200A0018380uLL >> ( (byte)(0x1D*b) >>2 ) ) ; }  

/ *
Среда тестирования
* /

void setup() {Serial.begin(115200);  testJohnson();  test(); fastLog2upN(0);  }

void loop() {   delay(250);  // return ;
  [](byte GrayX2){  Serial.print( GrayX2 ^ 0x0F ? "" : baq(GrayX2)+"\n"); 
   analogWrite( D5, (int []) { 0, 1200,    0, 300 }  [ GrayX2      & 0x3 ] );
   analogWrite( D6, (int []) { 0,    0, 1200, 300 }  [ GrayX2      & 0x3 ] );
   analogWrite( D7, (int []) { 0, 1200,    0, 300 }  [ GrayX2 >> 2 & 0x3 ] );
   analogWrite( D8, (int []) { 0,    0, 1200, 300 }  [ GrayX2 >> 2 & 0x3 ] ); } 
//                                    (  tooGray(           b              )  );
                                      (  tooGray( [](byte n) { return n; } )  );
}

/ ===========================================================================
Предупреждение:
-----------
Алгоритм OP не эффективен.

  Keep binary counter A
  Find B as A XOR (A+1)
  Bit to change is LowestBitSet in B

, как видно при кодировании как:
* /

void test(){
 static byte C=0, bits=0; 
 Serial.println((String)"\n  "+(3^2+1)+"  "+(3^(2+1))+"  "+((3^2)+1) );
 Serial.println(
  "\n                                         manifested by an        actual               "
  "\n                                        obstinate perverse       bit to               " 
  "\n                                              psyche              flip           check" 
  "\n      A            A+1         A ^ A+1       B^=A^A+1         (A^A+1)+1>>1            ");
 for(int intifiny=32, A=0; intifiny--; A=++A%15)  // sort a go infinite ... an epiphany!
  Serial.println( (String) pad(  b(  bits ^=  b( b(A) ^ b(A+1) )  )   ) +"    "+ 
                    baq( (A^A+1)+1>>1 ) +"    "+ baq( C^=(A^A+1)+1>>1 )  +"    "
                                              //       "| "+   pad(A)+" "+ pad(bits)
                    );
  Serial.println(
   "                                      |                                    "
   "\n                                     BITS:                                 " 
   "\n                              Bit Isn't This Silly                         " 
   "\n                               Bit Is Totally Set    (A ^ A+1) & -(A ^ A+1) == 1 ALWAYS " 
 "\n\n non-binary Gray codes?                                                    "
   "\n  {-1,0,1} balanced ternary, factoroid (factoradic), {0,-1} negated binary \n");
}  //  https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm  

//   some service routines  ...

String cut(byte sz, String str) { return     str.substring(str.length()-sz)   ; }
String pad(byte n             ) { return cut( 4, "         " + String(n,DEC) ); }
String baq(byte n             ) { return cut( 9, "........." + String(n,BIN) ); }
void   q  (                   ) {          /*  PDQ QED PQ's */         } 
void   p  (         String str) { Serial.print( "  " + str + "   " ) ; }
byte   d  (byte n             ) {  p(pad(n));         return n       ; }   
byte   b  (byte n             ) {  p(baq(n));         return n       ; }  

/ *
Пример вывода:

                                                                flip  bit                      
                                                               "correctly"    confirm?           
      A            A+1         A ^ A+1       B^=A^A+1         (A^A+1)+1>>1                 
  ........0     ........1     ........1     ........1      1    ........1    ........1   |    0    1
  ........1     .......10     .......11     .......10      2    .......10    .......11   |    1    2
  .......10     .......11     ........1     .......11      3    ........1    .......10   |    2    3
  .......11     ......100     ......111     ......100      4    ......100    ......110   |    3    4
  ......100     ......101     ........1     ......101      5    ........1    ......111   |    4    5
  ......101     ......110     .......11     ......110      6    .......10    ......101   |    5    6
  ......110     ......111     ........1     ......111      7    ........1    ......100   |    6    7
  ......111     .....1000     .....1111     .....1000      8    .....1000    .....1100   |    7    8
  .....1000     .....1001     ........1     .....1001      9    ........1    .....1101   |    8    9
  .....1001     .....1010     .......11     .....1010     10    .......10    .....1111   |    9   10
  .....1010     .....1011     ........1     .....1011     11    ........1    .....1110   |   10   11
     etc.                             |                                    
                                     BITS:                
                              Bit Isn't This Silly             Houston; We have a (an-other) problem    
                               Bit Is Totally Set                    
                        (A ^ A+1) & -(A ^ A+1) == 1 ALWAYS           

Любопытно?
-----------
Следующий кодочень очень грубый метод, который может ускорить поиск подходящих подсчитанных битовых кандидатов для вычисления log 2^n (в базе 2, т. е. n).
* /

byte fastLog2upN(byte b){                            //  b==2^N
    for(long long int i=8, b=1; --i;    )
        Serial.println((int)(0x0706050403020100uLL / (b*=0x100)),HEX)  ; 
    for( int i=9, b=1; --i;b*=2)        //   3A = 1D*2
        Serial.println( 
          (String)      (                           (b>>4 | b>>2 | b>>1) & 7   )   +   "    \t" + 
                        (                                   (0xB8*b) >>8 & 7   )   +   "    \t" + 
                        (                                   (0xB8*b) >>7 & 7   )   +   "    \t" + 
                        (                                   (0x1D*b) >>4 & 7   )   +   "    \t" + 
                        (                                   (0x0D*b) >>3 & 7   )   +   "   |\t" + 
                        (                            ((byte)(0x9E*b) >>2       ) ) +   "    \t" + 
                 (byte) ( 0x07070301C0038007uLL >>   ((byte)(0x9E*b) >>2       ) ) +   "    \t" + 
                        (                            ((byte)(0x1E*b) >>2       ) ) +   "    \t" + 
                 (byte) (  0x7070001C0038307uLL >>   ((byte)(0x1E*b) >>2       ) ) +   "    \t" + 
                        (                            ((byte)(0x5E*b) >>2       ) ) +   "    \t" + 
                 (byte) (  0x703800183838307uLL >>   ((byte)(0x5E*b) >>2       ) ) +   "  \t| " + 
                        (                            ((byte)(0x3A*b))>>5       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>4       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>3       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>2       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>1       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>0       )   +   "  \t| " + 
                 (byte) ( 0x0203040601050007uLL >> 8*((byte)(0x3A*b) >>5       ) ) +   "    \t" + 
          String((byte) ( 0x0706050403020100uLL >>   ((byte)(0x3A*b) >>4       ) ),HEX ) + "\t" + 
          String((byte) (       0x0020010307uLL >>   ((byte)(0x3A*b) >>3       ) ),HEX ) + "\t" + 
          String((byte) ( 0x10300200A0018007uLL >>   ((byte)(0x3A*b) >>2       ) ),HEX ) + "\t|" + 
                        (                            ((byte)(0x1D*b))>>2       )   +   "    \t" + 
                 (byte) ( 0x10710700E0018380uLL >>   ((byte)(0x1D*b) >>2       ) ) +   "    \t" + 
                 (byte) ( 0x10310200A0018380uLL >>   ((byte)(0x1D*b) >>2       ) ) +   "  \t| " + 
       "") ;
   } 

/ *

javascript: x=6; y=4; n=511; ra=[]; s="<pre>x"; 
   while(n--)for(i=5;i;i=i==3?2:--i){ 
      j=0; 
      for(b=1;b<129;b*=2) ra[j++]=((n*b)&0xFF)>>i;
      ra.sort(function(a, b){return a-b});
      if ( tf=ra[--j] < 64 &&  ra[1]>ra[0]+x ) 
         while( --j && ( tf = (ra[j]>ra[j-1]+x) || ( ra[j]<ra[j-1]+y  && ra[j+1]>ra[j]+x) ) );
      if(tf) s+="\n "+n.toString(16)+" >> "+i+" \t"+ra.join("  "); 
   }; 
  s;

// many >>2 's   to do: sledge hammer creation of acceptable bit pattern solutions
// only want btf's - Bits That Fit (in 8 bytes): (tf=ra[j-1] < 64)
// program proximity control so no BS (Brain Strain!): tf = (ra[j]<ra[j-1]+x) || (ra[j]<ra[j-2]+y) 
// for debug: s+="\n "+n.toString(16)+" >> "+i; 
// for debug: s+="\n" +tf+"\t"+ra[j]+"\t"+ra[j-1]+"\t"+ra[j-2];  

* /

0 голосов
/ 29 июня 2013

LowestBitSet(A ^ (A+1)) всегда 0, если вы не работаете в IBM. Я думаю, что вы имеете в виду HighestBitSet(), что примерно равно log_2().

Хит с переворотом , непосредственно предшествующий , тот, который связан с AShelly, будет гораздо более осуществимым на 8-битном микро.

Это должно сделать ваш оригинальный алгоритм довольно практичным, генерируя { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, ... }.

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

0 голосов
/ 10 января 2011

Бинарный отражающий серый код см. В этом ответе , где указан эффективный способ вычисления кода N.
XOR с предыдущим кодом, чтобы получить значение, в котором установлен только изменяемый бит.
Затем вы можете использовать этот бит Twiddling Hack (случай, когда "v - степень 2"), чтобы найти битовый индекс только с 3 операциями и таблицей из 32 записей.

Псевдокод выглядит примерно так:

  n = lastCode = 0
  increment:
     n+=1
     newCode = GrayCode(n)
     delta = newCode XOR oldCode
     bitToToggle = BitIndex(delta)
     old code = new code
     GOTO increment;
0 голосов
/ 10 января 2011
...