曼彻斯特编码数据流


14

曼彻斯特编码是一种用于无线电通信的电信协议,该协议可确保按固定间隔进行位转换,因此接收器可以从数据本身中恢复时钟速率。它使比特率翻倍,但价格便宜且易于实现。它被业余无线电运营商广泛使用。

这个概念非常简单:在硬件级别,时钟和数据线只是简单地异或在一起。在软件中,这被描述为将位的输入流转换为双倍速率的输出流,每个输入“ 1”转换为“ 01”,每个输入“ 0”转换为“ 10”。

这是一个容易解决的问题,但由于其比特流的性质,因此对许多实现开放。即,编码在概念上是逐位处理而不是逐字节处理。因此,我们都同意字节序,输入的最低有效位成为输出的最低有效字节。

打高尔夫球的时间!编写一个函数,给定任意长度的字节数组,该函数返回该曼彻斯特编码数据的数组。

在位流中,应将输入和输出视为低位字节序,最低有效字节在前,最低有效BIT在前。

ASCII位流图

bit #      5 4 3 2 1 0                                5  4  3  2  1  0
IN ------- 1 0 1 0 1 1 ---> [manchester encoder] ---  01 10 01 10 01 01 ----> OUT

例子

Example 1 (hex):
       LSB              MSB     <-- least sig BYTE first
IN : [0x10, 0x02]  
OUT: [0xAA, 0xA9, 0xA6, 0xAA]  

Example 1 (binary):
      msb  lsb                      msb  lsb  <-- translated hex, so msb first
BIN: [00010000, 00000010]                     <-- least sig NIBBLE...
BIN: [10101010, 10101001, 10100110, 10101010] <-- becomes least sig BYTE
         LSB                           MSB

Example 2
IN :  [0xFF, 0x00, 0xAA, 0x55]  
OUT: [0x55, 0x55, 0xAA, 0xAA, 0x66, 0x66, 0x99, 0x99]

Example 3
IN : [0x12, 0x34, 0x56, 0x78, 0x90]  
OUT: [0xA6, 0xA9, 0x9A, 0xA5, 0x96, 0x99, 0x6A, 0x95, 0xAA, 0x69] 

Example 4
IN : [0x01, 0x02, 0x03, 0xF1, 0xF2, 0xF3]  
OUT: [0xA9, 0xAA, 0xA6, 0xAA, 0xA5, 0xAA, 0xA9, 0x55, 0xA6, 0x55, 0xA5, 0x55]

规则

  • 解决方案仅需要将输入转换为输出的算法。
  • 获取输入和打印输出不是解决方案的必需部分,但可以包括在内。如果您的解决方案中未包含测试/打印代码,则建议您提供。
  • 输入是一个8位字节的数组(无论用您选择的语言可能意味着什么),而不是文本字符串。如果方便,可以使用字符串作为存储格式,但是必须支持不可打印的字符(例如0xFF)。如有必要,输入也可以取一个长度。
  • 输出的内存必须由您的例程分配,而不是提供。 编辑:不必要的要求
  • 输出也是一个8位字节的数组,如果需要的话,还有一个长度。
  • 必须至少支持16KB输入
  • 性能一定不能太糟糕:16KB <10s
  • 内存中的最低有效字节优先。

旁道挑战

  • 通过证明您的代码更快,内存效率更高或产生更小的二进制代码来挑战另一个用户的答案!

打高尔夫球!最短的代码胜出!


2
“输出的内存必须由您的例程分配,而不是提供。” 由于许多语言具有完全自动的内存分配功能,因此这似乎是一个很奇怪的要求。
aaaaaaaaaaaaaa

到底您拥有什么来使用这种离奇的位阶?
彼得·泰勒

当您考虑将其用于物理介质时,位顺序是有意义的。该算法适用于在空中传播的单个比特流。我们必须将其存储在内存中,并且必须编写十六进制的msb-> lsb,这一事实使得跟踪它变得有些棘手。
mrmekon 2011年

Answers:


6

GolfScript 28个字符

{2{base}:|~4|43691-~256|~\}%

等效版本,不会混淆优化:

{2base 4base 43691-~256base~\}%

该代码接受输入为整数数组,并返回ditto。

对于数组中的每个数字,该数字将转换为以2为底的数组形式,然后将其转换回数字,就好像它是以4为底的数字一样。这样的效果是,每个位之间的位以0隔开。然后从数字中减去43691,并对结果进行二进制求逆,这等效于从43690中减去数字(43690 = 0b1010101010101010010)。然后将数字转换为基数为256的数组将其分为两部分,对该数组进行分解,然后将两个结果数字的顺序取反。

输入示例:

[1 2 3 241 242 243]

输出示例:

[169 170 166 170 165 170 169 85 166 85 165 85]

那太短了,而且非常聪明!尽管它似乎不符合<10s性能建议中的16KB,至少对我来说是这样。您的双核Mac电脑上需要43s来转换16384 1的数组。相比之下,我的大型(2419个字符)python实现占用16KB的空间为0.06s。
mrmekon

在我的机器上(Win 7),它花费不到5秒,并且大部分时间是将数组转换为文本输出,据我所知,这不是要求的一部分,但是GolfScript会自动将剩下的所有内容执行后放在堆栈上。可以简单地使代码删除结果而不是打印结果(在代码末尾添加;)。如果您想查看输出(尽管这不是问题说明的一部分。),我知道两种提高速度的技巧,将其重定向到文件,并使用打印命令将其显式打印为小块:{2{base}:|~4|43691-~256|~p p}%
aaaaaaaaaaaaaa

在Ubuntu虚拟机上(在Windows上),我得到16kb的8s。在具有更好CPU的Mac上,耗时1分18秒。我想红宝石比OSX随附的红宝石要慢
得多

看来我机器上的红宝石打印速度慢得令人作呕。关闭打印仅2s,而Ruby 1.9则关闭(对于本机OSX版本为5s)。那好多了!
mrmekon 2011年

3

c-224个字符

我相信这是有效的,包括自删除以来分配的内存需求。

#include <stdlib.h>
int B(char i){int16_t n,o=0xFFFF;for(n=0;n<8;++n)o^=((((i>>n)&1)+1))<<(2*n);
return o;}char* M(char*i,int n){char*o=calloc(n+1,2),*p=o;do{int r=B(*i++);
*p++=0xFF&r;*p++=(0xFF00&r)>>8;}while(--n);return o;}

该代码的工作部分是在每个字符的位上循环,请注意((bit + 1)异或3)是输出位对,并应用了大量的移位和屏蔽逻辑来使所有内容对齐。

正如c不会那样,它以字符形式处理数据。测试脚手架将不接受0字节(因为c将它们视为字符串结尾),但是工作代码没有这种限制。

通过内联复制字节转换工作,可能会花费更多精力。

测试运行(使用改进的测试支架):

$ gcc -g manchester_golf.c
$ ./a.out AB xyz U
'AB':
[ 0x41, 0x42 ]
[ 0xa9, 0x9a, 0xa6, 0x9a ]
'xyz':
[ 0x78, 0x79, 0x7a ]
[ 0x6a, 0x95, 0x69, 0x95, 0x66, 0x95 ]
'U':
[ 0x55 ]
[ 0x99, 0x99 ]

已注释,更少依赖机器,并且带有测试支架

/* manchester.c
 *
 * Manchester code a bit stream least significant bit first
 *
 * Manchester coding means that bits are expanded as {0,1} --> {10, 01}
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>

/* Caller must insure that out points to a valid, writable two byte
   buffer filled with 0xFF */
int16_t manByte(char i){
  int16_t n,o=0xFFFF;
  printf("Manchester coding byte 0x%hx...\n",i);
  for(n=0; n<CHAR_BIT; ++n)
    o ^= (
      (
       (
        (i>>n)&1) /* nth bit of i*/
       +1) /* +1 */
      ) <<(2*n) /* shifted up 2*n bits */ 
      ;
  printf("\tas 0x%hx\n",o);
  return o;
}

char* manBuf(const char*i, int n){
  char*o=calloc(n+1,2),*p=o;
  do{
    int16_t r=manByte(*i++);
    *p++= 0xFF&r;
    *p++=(0xFF00&r)>>8;
  } while(--n);
  return o;
}

void pbuf(FILE* f, char *buf, int len){
  int i;
  fprintf(f,"[");
  for(i=0; i<len-1; i++)
    fprintf(f," 0x%hhx,",buf[i]);
  fprintf(f," 0x%hhx ]\n",buf[len-1]);
}

int main(int argc, char**argv){
  int i;
  for(i=1; i<argc; i++){
    int l=strlen(argv[i]);
    char *o=manBuf(argv[i],l);
    printf("'%s':\n",argv[i]);
    pbuf(stdout,argv[i],l);
    pbuf(stdout,o,l*2);
    free(o);
  }
  return 0;
}

3

J,36

,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0)

说明概要(请参阅J词汇表作为参考):

  • ,@:(3 :'...'"0)将...作为y应用于每个输入“字节”,从而每个产生两个字节(整数)。结果被展平,
  • y#:~8#22 2 2 2 2 2 2 2 #: y与y的8个最低有效基数2 等效,或为向量。
  • 4|. 通过旋转4个位置来交换前4位和后4位。
  • (,.~-.) 相当于 3 :'(-. y) ,. y'或不参数“缝制”到参数(采用形状8 2)。
  • #.2 8$, 展平结果以提供比特流,将其整形为2行8,然后从基数2转换。

用法示例(J,交互式):

    ,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0) 1 2 3 241 242 243
,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0) 1 2 3 241 242 243
169 170 166 170 165 170 169 85 166 85 165 85

速度信息(J,交互式):

   manchester =: ,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0)
manchester =: ,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0)
   data =: 256 | i. 16384
data =: 256 | i. 16384
   100 (6!:2) 'manchester data'
100 (6!:2) 'manchester data'
0.243138

16kb的平均时间不到0.25秒,即Intel Core Duo 1.83Ghz或类似水平。


3

Haskell,76个字符

import Bits
z a=170-sum[a.&.p*p|p<-[1,2,4,8]]
y a=[z a,z$a`div`16]
m=(>>=y)

测试运行:

> testAll 
input      [10, 02]
encoded    [AA, A9, A6, AA]
  pass
input      [FF, 00, AA, 55]
encoded    [55, 55, AA, AA, 66, 66, 99, 99]
  pass
input      [12, 34, 56, 78, 90]
encoded    [A6, A9, 9A, A5, 96, 99, 6A, 95, AA, 69]
  pass
input      [01, 02, 03, F1, F2, F3]
encoded    [A9, AA, A6, AA, A5, AA, A9, 55, A6, 55, A5, 55]
  pass

性能完全符合规范。在我较旧的笔记本电脑上,在约1.2秒内达到1MB。之所以会受苦,是因为输入在列表中转换,而不是在列表中转换ByteArray

> dd bs=1m count=1 if=/dev/urandom | time ./2040-Manchester > /dev/null
1+0 records in
1+0 records out
1048576 bytes transferred in 1.339130 secs (783028 bytes/sec)
        1.20 real         1.18 user         0.01 sys

源代码2040-Manchester.hs包括命令行过滤器的代码,测试和主要功能。


3

OCaml +电池138117个字符

let m s=Char.(String.(of_enum[?chr(170-Enum.sum[?d land
p*p|p<-List:[1;2;4;8]?])|c<-enum s/@code;d<-List:[c;c/16]?]))

测试:

let hex s = String.(enum s/@(Char.code|-Printf.sprintf "%02x")|>List.of_enum|>join" ")

结果是:

m "\x12\x34\x56\x78\x90" |> hex;;
- : string = "a6 a9 9a a5 96 99 6a 95 aa 69"
m "\x10\x02" |> hex;;
- : string = "aa a9 a6 aa"
m "\xFF\x00\xAA\x55" |> hex;;
- : string = "55 55 aa aa 66 66 99 99"
m "\x12\x34\x56\x78\x90" |> hex;;
- : string = "a6 a9 9a a5 96 99 6a 95 aa 69"
m "\x01\x02\x03\xF1\xF2\xF3" |> hex;;  
- : string = "a9 aa a6 aa a5 aa a9 55 a6 55 a5 55"

作为基准,具有:

let benchmark n =
  let t = Unix.gettimeofday() in
  assert(2*n == String.(length (m (create n))));
  Unix.gettimeofday() -. t

我得到:

# benchmark 16_384;;
- : float = 0.115520954132080078

在我的MacBook上。


1

Python,87个字符

M是问题中要求的功能。它要求N每个半字节并将所有内容拼接回列表。

N=lambda x:170-(x&1|x*2&4|x*4&16|x*8&64)
M=lambda A:sum([[N(a),N(a>>4)]for a in A],[])

print map(hex,M([0x10,0x02]))
print map(hex,M([0xff,0x00,0xaa,0x55]))
print map(hex,M([0x12, 0x34, 0x56, 0x78, 0x90]))
print map(hex,M([0x01, 0x02, 0x03, 0xF1, 0xF2, 0xF3]))

产生

['0xaa', '0xa9', '0xa6', '0xaa']
['0x55', '0x55', '0xaa', '0xaa', '0x66', '0x66', '0x99', '0x99']
['0xa6', '0xa9', '0x9a', '0xa5', '0x96', '0x99', '0x6a', '0x95', '0xaa', '0x69']
['0xa9', '0xaa', '0xa6', '0xaa', '0xa5', '0xaa', '0xa9', '0x55', '0xa6', '0x55', '0xa5', '0x55']

1

APL(Dyalog扩展),22字节

∊(⌽(2256)⊤43690-4⊥⊤)¨

在线尝试!

GolfScript答案的端口。

∊(⌽(2256)⊤43690-4⊥⊤)¨       Monadic train:
  ⌽(2256)⊤43690-4⊥⊤         Define a helper function taking an integer n:
                               Convert n to base 2. Monadic  is an Extended feature.
                  4            Convert the result from base 4.
                                This puts the 1 digits of n 
                                in odd indices of the intermediate result.
            43960-              Subtract from 43690.
    (2256)⊤                    Convert to 2 base-256 digits, corresponding to
                                nibbles of n.
                              Reverse the order of these bytes.
 (                          Call the helper function for each element of the input
                             and flatten the results into a list.

0

C,164字节

接收一系列十六进制字节,然后转换为曼彻斯特二进制流。

#include <stdio.h>
main(int c,char **v){int i,b,x,j=0;while(++j<c{sscanf(v[j],"%x",&b);x=b^0xff;for(i=9;--i;){printf("%d%d",x&1,b&1);x=x>>1;b=b>>1;}printf("\n");}}

#include <stdio.h>
main(int c,char **v){
int i,b,x,j=0;
while(++j<c){
    sscanf(v[j],"%x",&b);
    x=b^0xff;
    for(i=9;--i;){
        printf("%d%d",x&1,b&1);
        x=x>>1;b=b>>1;}
    printf("\n");}}

测试:

./a.out 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0

输出:

1010101010101010
0110101010101010
1001101010101010
0101101010101010
1010011010101010
0110011010101010
1001011010101010
0101011010101010
1010100110101010
0110100110101010
1001100110101010
0101100110101010
1010010110101010
0110010110101010
1001010110101010
0101010110101010
1010101010101010
1010101001101010
1010101010011010
1010101001011010
1010101010100110
1010101001100110
1010101010010110
1010101001010110
1010101010101001
1010101001101001
1010101010011001
1010101001011001
1010101010100101
1010101001100101
1010101010010101
1010101001010101

16kb测试数据集生成器:

test_data.c:

#include <stdio.h>
void main()
{
int i=16*1024;
while(i--)
{
    printf("0x%02x ", i&0xFF);
}
printf("\n");
}

cc test_data.c -o test_data

1.6G i5dual核心时间试用:

time ./a.out `./test_data` > test.out 
real    0m0.096s
user    0m0.090s
sys 0m0.011s

不错的第一篇文章,但我们通常不会尝试混淆我们的代码。是的较短,更难阅读否。
Rɪᴋᴇʀ

0

PHP,156字节

function f($i){foreach($i as$n){$b=str_split(str_replace([0,1,2],[2,'01',10],
str_pad(decbin($n),8,0,0)),8);$o[]=bindec($b[1]);$o[]=bindec($b[0]);}return$o;}

给定输入[0, 1, 2, 3, 4, 5],它返回:

[170, 170, 169, 170, 166, 170, 165, 170, 154, 170, 153, 170]

它在0.015秒内编码16 KiB数据,在大约0.9秒内编码1 MiB数据。

可以在在Github 上的“ 代码高尔夫解决方案”页面上找到未高尔夫的代码,另一个实现(更长,大约慢一倍)和测试用例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.