餡子付゛録゛

ソフトウェア開発ツールの便利な使い方を紹介。

Rで解いていないFizzBuzz問題

Rで解くFizzBuzz問題」を見ていて理解できる事は、Rで高速なコードを書くのはトリッキーだと言う事ですね。素直にCで実装した方が速いかも知れないと言うことで、試してみました。
速度的には、Rで最も速かった「裏 RjpWiki 変態チック版」の約1.5倍という所で、時間にして3割強、短縮できています。Rの速度はインタープリッタの言語としては、かなり驚異的な数字でしょう。Rもループと条件分岐を避ければ、かなり高速になるようです。
試したコードは以下になります。数字の文字列化のオーバーヘッドを多少は押さえていますが、オーソドックスに実装していると思います。なお、仮想マシンを使っていたためかベンチマークの速度は安定しませんでした。

R-FizzBuzz.c

#include <R.h>
#include <Rinternals.h>

char* itoa(int value, char* result, int base);

/*
 * FizzBuzz問題を解く
 * args: 配列の大きさ
 * return: 配列
 */
SEXP FizzBuzz(SEXP n)
{
  SEXP ans; /* 戻り値 */
  int len, i, m;
  char buf[64];

  len = INTEGER(n)[0];

  PROTECT(ans = allocVector(STRSXP, len));
  for(i = 0; i < len; i++) {
    m = i + 1;
    if(!(m % 3)){
      if(!(m % 5)){
        SET_STRING_ELT(ans, i, mkChar("FizzBuzz"));
      } else {
        SET_STRING_ELT(ans, i, mkChar("Buzz"));
      }
    } else if(!(m % 5)){
      SET_STRING_ELT(ans, i, mkChar("Fizz"));
    } else {
      itoa(m, buf, 10);
      SET_STRING_ELT(ans, i, mkChar(buf));
    }
  }
  UNPROTECT(1);
  return(ans);
}

char* itoa(int value, char* result, int base) {
  if (base < 2 || base > 36) { *result = '\0'; return result; }

  char* ptr = result, *ptr1 = result, tmp_char;
  const char* num = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz";
  int tmp_value;

  do {
    tmp_value = value;
    value /= base;
    *ptr++ = num[35 + (tmp_value - value * base)];
  } while ( value );
  if (tmp_value < 0) *ptr++ = '-';
  *ptr-- = '\0';
  while(ptr1 < ptr) {
    tmp_char = *ptr;
    *ptr--= *ptr1;
    *ptr1++ = tmp_char;
  }
  return result;
}

コンパイル

R CMD SHLIB R-FizzBuzz.c

Rから呼び出す

dyn.load(paste("R-FizzBuzz", .Platform$dynlib.ext, sep = ""))
fb8 <- function(n){
  .Call("FizzBuzz", as.integer(n))
}
N <- 1000000
gc();gc();system.time({ fb8(N) })