/*
如之前发布的答案https://dev59.com/t2445IYBdhLWcg3w5OEH所述,
使用约翰逊计数器格雷码非常简单:
Number_of_Bit_To_Flip = ++Number_of_Bit_To_Flip % Total_Number_of_Counter_Bits
该代码在每个事件发生时执行。
否则,使用二进制反射格雷码和4字节的2进制计数器n
...
方法1 - 使用表格*/
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,
};
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]];
return (byte[]){ 0 + twos[ctr[0]] ,
8 + twos[ctr[1]] ,
16 + twos[ctr[2]] ,
24 + twos[ctr[3]] ,
31 + twos[ctr[0]] } [ !ctr[0]*( 1+
!ctr[1]*( 1+
!ctr[2]*( 1+
!ctr[3] ) ) ) ];
}
/方法二 - 无需表格 */
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] ] ;
}
/*
对于比特兔和货物崇拜者来说,无需继续阅读。
上述代码的执行效率取决于 n[0], n[1], ...
作为固定地址在编译时计算出来,这是相当常规的。此外,使用按需调用优化编译器将加快数组内容,因此只需要计算一个索引值。这种编译器的复杂性可能会缺失,但手动组装原始机器代码很容易实现(基本上是一个 switch、computed 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个索引操作。
执行第二种方法(经过最优编译)的最终代码,在处理总数的7/8时,使用了7个逻辑操作(
|| & ! < -
最后一个是二进制补码)、1个算术运算符(
+
)和5个计算的索引操作。但还有1/8的情况,除了一个实例之外,需要8个逻辑、1个加法和6个计算的索引操作。
不幸的是,神的启示没有呈现任何代码灵感。这是本篇文章作者创作过程的缩写故事。
这是如何完成的,需要进行关键性的初步调查,具体记录在此:
https://dev59.com/t2445IYBdhLWcg3w5OEH#42846062.
然后使用连续的细化过程派生出代码,
首先评估了该帖子中的算法。
具体来说,是这个答案: https://dev59.com/t2445IYBdhLWcg3w5OEH#4657711 .
通过将返回计算减少到单个加法和两个索引操作,
可以鲜明而显著地强调此算法的时间变量执行从循环开销中。
*/
byte bit_change(byte ctr[4]) {
static byte ones[256];
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;
unsigned char i; for (i = 0; i < 4; i++) {
unsigned char x = counter[i];
if (x) {
x ^= x - 1;
return 8 * i + ones[x];
} }
byte x; for (byte i = 0; i < 4; i++)
if (x = counter[i])
return i<<3 + ones[x ^ x - 1];
}
/--------------------------------------------------------------
--------------------------------/
(注:此为HTML标签,无需翻译)
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,
};
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]];
break;
}
for (; i < 4; i++) if (ctr[i]) ;
return ansr;
}
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;
}
byte f2(byte counter[4]){
byte i, ansr=31;
for (i = 0; i < 4; i++) {
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]);
return ansr;
}
byte f3(byte counter[4]){
switch (!counter[0]*( 1 +
!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://dev59.com/t2445IYBdhLWcg3w5OEH#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://dev59.com/t2445IYBdhLWcg3w5OEH#42846062),因为不可能代表其他个体的认知能力发言。
https://dev59.com/t2445IYBdhLWcg3w5OEH#42846062的内容对于背景信息至关重要,反映了解决这个问题所需的非常个人化的追求完美机制。毫无疑问,环境和大量材料是令人生畏的,但这正是获取足够洞察力、库存、轶事先例等的个人方法,以推断和插值出一个特定的答案,即“哪个程序完全符合标准”。此外,正是这种“追逐”激发和激励着(也许是病态的)心理投入时间和精力,以满足好奇心的探究之旅。
*/
void setup() { }
void loop() { }