ページ 11

文字列

Posted: 2010年8月25日(水) 18:56
by しんご
a[0] => 0
:
a[0] => 9
a[0] => a
:
a[0] => z
a[0] => A
:
a[0] => Z
a[0][1] => 00
:
a[0][1] => 09
a[0][1] => 0a
:
a[0][1] => 0z
a[0][1] => 0A
:
a[0][1] => 0Z

最初に希望の桁数を入力して
その分だけ上記のようにしたいです。

【例】
入力→5
表示:0~ZZZZZ


自分でもいろいろ考えてみたのですが
アルゴリズム?が自分にとって複雑すぎてわかりません。
よろしくお願いします。

Re:文字列

Posted: 2010年8月25日(水) 19:12
by バグ
逆に確認するけど、

1:C言語の理解度はどの程度?

2:使っていい関数と駄目な関数は?

3:開発環境に制限はある?

4:入力値の範囲は?(最大値、最小値のことね)

5:ようは大文字&小文字で62進数っぽく(0~9、a~z、A~Zで桁上がり)表示したいってこと?



もしそうだとしたら、2桁の場合は

00~09、0a~0z、0A~0Z
10~19、1a~1z、1A~1Z
20~29、2a~2z、2A~2Z
:
:
X0~X9、Xa~Xz、XA~XZ
Y0~Y9、Ya~Yz、YA~YZ
Z0~Z9、Za~Zz、ZA~ZZ

じゃないの?

Re:文字列

Posted: 2010年8月25日(水) 19:50
by しんご
> 1:C言語の理解度はどの程度?
参考書を二度ほど読み終わり現在は色々試しています。

> 2:使っていい関数と駄目な関数は?
特にありませんがWinで使用します。

> 3:開発環境に制限はある?
VC++ 2010を使用しています。

> 4:入力値の範囲は?(最大値、最小値のことね)
あまり長くすると処理に時間が掛かりますよね?
単純に考えて10桁だとすると
(26+26+10)^10-1
という計算になります。
何通りあるのでしょうか・・・
とりあえず2桁から8桁くらいでやってみて
時間を計りつつ増やせそうなら自分で範囲を広げて行こうと思います。

> 5:ようは大文字&小文字で62進数っぽく(0~9、a~z、A~Zで桁上がり)表示したいってこと?
そうです。


> もしそうだとしたら、2桁の場合は

> 00~09、0a~0z、0A~0Z
> 10~19、1a~1z、1A~1Z
> 20~29、2a~2z、2A~2Z
> :
> :
> X0~X9、Xa~Xz、XA~XZ
> Y0~Y9、Ya~Yz、YA~YZ
> Z0~Z9、Za~Zz、ZA~ZZ

> じゃないの?
そうです。
それを指定桁数分やりたいのです。

Re:文字列

Posted: 2010年8月25日(水) 21:46
by ookami
例えば「10進数の5」を2進数で表示するプログラムはかけますか?(%bなしで)

Re:文字列

Posted: 2010年8月25日(水) 22:39
by みけCAT
まず0から9、aからz、AからZを順番にchar型配列に格納します。(cとします。)
表示する桁数のint型配列を用意します。(aとします。)
aを0で初期化します。
表示する文字列用のバッファを用意します。(sとします。)

表示する文字列の作成
s[0]=0;
for(i=0;i<桁数;i++;){
sprintf(s,"%s%c",s,c[a]);
}
sを表示します。

aの最後を1増やします。
その結果62以上になったら0にしてひとつ前を1増やします。
62以上になったら同じようにします。
最初が62を越えたら終了します。

表示する文字列を作成して表示します。

繰り返します。

間違っていたらごめんなさい。

Re:文字列

Posted: 2010年8月25日(水) 23:59
by バグ
結構、まわりくどいやり方になっちまった(^_^;)
表示が違うけど、それは自分で修正してくださいなm(__)m
#include <math.h>
#include <stdio.h>
#include <string.h>

#define STR_MAX_LEN        10 + 1

const char parts[/url] = {    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                        'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                        'u', 'v', 'w', 'x', 'y', 'z',
                        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
                        'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                        'U', 'V', 'W', 'X', 'Y', 'Z' };

void reverse(int n, char* str)
{
    if (n >= 0)
    {
        putc(*(str + n), stdout);
        reverse(n - 1, str);
    }
}

void convert(int digit)
{
    char str[STR_MAX_LEN] = {0, };
    int i = 0;
    unsigned long long cnt = 0;
    unsigned long long tmp = 0;
    unsigned long long max = 0;

    max = (unsigned long long)(pow((long double)(62), digit) - 1);

    for (cnt = 0 ; cnt <= max; ++cnt)
    {
        tmp = cnt;

        for (i = 0; i < digit; ++i)
        {
            str = parts[tmp % 62];
            tmp /= 62;

            if (tmp == 0)
                break;
        }

        printf("%I64u = ", cnt);
        reverse((int)strlen(str), str);
        putc('\n', stdout);
    }
}

int main(void)
{
    convert(5);
    return 0;
}

Re:文字列

Posted: 2010年8月26日(木) 09:14
by バグ
こちらの方が色々と扱いやすいかな?
#include <math.h>
#include <stdio.h>
#include <string.h>

#define STR_MAX_LEN        10 + 1

const char parts[/url] = {    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                        'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                        'u', 'v', 'w', 'x', 'y', 'z',
                        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
                        'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                        'U', 'V', 'W', 'X', 'Y', 'Z' };

void reverse(int n, int m, const char* src, char* dest)
{
    if (n >= 0)
    {
        *(dest + m) = *(src + n);
        reverse(n - 1, m + 1, src, dest);
    }
}

/*
    val  = 変換したい値
    base = 基数(10進数なら10、16進数なら16、62進数なら62)
    dig  = 最大文字数
    str  = 変換後の文字列が格納される領域へのポインタ
*/
void convert(const unsigned long val, const int base, const int dig, char* str)
{
    char buf[STR_MAX_LEN] = {0, };
    int i = 0;
    unsigned long tmp = 0;

    tmp = val;

    for (i = 0; i < dig; ++i)
    {
        buf = parts[tmp % base];
        tmp /= base;

        if (tmp == 0)
            break;
    }

    /* 文字列の反転 */
    reverse((int)strlen(buf) - 1, 0, buf, str);
}

int main(void)
{
    char* str[STR_MAX_LEN] = {0, };
    unsigned long i = 0;

    for (i = 0; i < 4000; ++i)
    {
        convert(i, 62, 10, str);
        printf("%s ", str);
    }

    return 0;
}

Re:文字列

Posted: 2010年8月26日(木) 09:45
by ookami
私も便乗させていただきたくw こんなのいかがでしょうか。

#include <stdio.h>
#include <string>

// i^pを返す
int ipow(int i,int p) {
int r=1;
while(p--) r*=i;
return r;
}

void main(void)
{
int digit=2;
const char *chartbl="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i,n=(int)strlen(chartbl),imax=ipow(n,digit);
const int BUFMAX=8;
char buf[BUFMAX]={0},*p;
for(i=0;i<imax;i++) {
p=buf+BUFMAX-2;
for(int j=i;j;j/=n) *p--=chartbl[j%n];
p++;
printf("%d : %s\n",i,p);
}
}

Re:文字列

Posted: 2010年8月26日(木) 13:56
by フリオ
 
 考えてみました。
少し書き直しました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int func(int m)
{
    const char *s = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const int n = (int)strlen(s);
    int *p;
    
    if(m <= 0) return 0;
    if(!(p = calloc(m, sizeof(int)))) return 0;
    while(1){
        int i;
        
        for(i = 0; i < m; ++ i) putchar(s[p]); putchar(' ');
        while(-- i >= 0 && ++ p == n) p = 0;
        if(i < 0) break;
    }
    free(p);
    return 1;
}

int main(void)
{
    func(3);
    return 0;
}

  画像

Re:文字列

Posted: 2010年8月26日(木) 18:41
by しんご
バグさん
ookamiさん
フリオさん

ありがとうございます。
とても参考になりました。

現在、希望の文字列になるまで何秒掛かるかを見たりしています。

そこで欲が出てしまったのですが・・・
今度は
①桁数を入力。
②始まりの文字列
③終わりの文字列
を入力して何秒掛かるのかを見てみたいと思いました。

【例】
桁数:5
始まりの文字列:0a5bc
終わりの文字列:0b6cd

自分でも色々やってみたのですが
どうも難しくてわかりません>_<

Re:文字列

Posted: 2010年8月26日(木) 18:53
by バグ
今度は逆の処理ですね。

つまり、62進数から10進数への変換処理をしてやる必要がありますね。

1桁目の数値は1~Z(1~61)
2桁目の数値は(1~Z)×62
3桁目の数値は(1~Z)×62×62
4桁目の数値は(1~Z)×62×62×62

ってな具合で任意の桁まで繰り返していき、全ての桁の数値の総和を求めれば10進数への変換は終了です。
この処理で始まりの値と終わりの値を求めてやり、forループとかで繰り返せばよいだけですね。


さて、ここまでは理解できますか?

Re:文字列

Posted: 2010年8月26日(木) 19:00
by ookami
> 希望の文字列になるまで何秒掛かるかを見たりしています。

取り急ぎ、かかっている時間のほとんどは、printfに費やされていると、指摘してみます。

Re:文字列

Posted: 2010年8月27日(金) 07:30
by しんご
ookamiさん

自分もそう思ったのですが
処理が長くなるとやはり変わってきますよ。

Re:文字列

Posted: 2010年8月27日(金) 07:49
by ookami
オブラートに包みすぎましたm(_ _)m

つまりですね、
その変換した文字列を、最終的に、どんなふうに使うんですか?

Re:文字列

Posted: 2010年8月27日(金) 08:47
by しんご
ookamiさん

どう使うかと聞かれると・・・
特に使い道は考えていません。。。
強いて言うのであれば
ループの回数によってどの程度処理速度が変わるのかを見てみたいのですが
ただ
for(;;) i++;
としただけではつまらないので
このように複雑にしようと思っています。

Re:文字列

Posted: 2010年8月27日(金) 08:59
by ookami
あー... そうなんですか?
えーと、じゃぁ...
ノーコメントです^^; 失礼しました。

Re:文字列

Posted: 2010年8月27日(金) 09:42
by しんご
バグさん

> 今度は逆の処理ですね。
>
> つまり、62進数から10進数への変換処理をしてやる必要がありますね。
>
> 1桁目の数値は1~Z(1~61)
> 2桁目の数値は(1~Z)×62
> 3桁目の数値は(1~Z)×62×62
> 4桁目の数値は(1~Z)×62×62×62
>
> ってな具合で任意の桁まで繰り返していき、全ての桁の数値の総和を求めれば10進数への変換は終了です。
> この処理で始まりの値と終わりの値を求めてやり、forループとかで繰り返せばよいだけですね。
>
>
> さて、ここまでは理解できますか?
>

色々やってみてるのですが
いまいちわかりません>_<
ちなみに
> 1桁目の数値は1~Z(1~61)
> 2桁目の数値は(1~Z)×62
> 3桁目の数値は(1~Z)×62×62
> 4桁目の数値は(1~Z)×62×62×62
これなんですけど
1~Z(1~61)
でいいのですか?
0~Z(0~61)
にならないのはなぜですか?

たとえば
「2Cz」という文字列は
1桁目・・・z(36)
2桁目・・・C(39*62=2418)
3桁目・・・2(3*62*62=11532)
なので
36+2418+11532=13986
ということでしょうか?
※0~Z(0~61)で計算してます。

同じように終わりの文字列を計算して
14986だったとしたら
while(13986 <= 14986) //変数ではなく例でわかりやすいように数字にしてます
という具合でループして
10進数をまた62進数にするといことですか?

Re:文字列

Posted: 2010年8月27日(金) 09:44
by しんご
ookamiさん
逆にどういうことに使えそうですかね?
この先いろいろ改造していきたいので
これを使ったおもしろいものがあったら
教えてください^^

Re:文字列

Posted: 2010年8月27日(金) 10:23
by バグ
>>しんごさん
おっと、失礼しました。
たしかに1~Z(1~61)ではなく、0~Z(0~61)の間違いですね。

とりあえず書いてみましたので、解析してみてください。
あまり深くデバッグしていないのでバグが潜んでいるかもしれませんが、ご容赦を。

※unsigned long longを多用しているのは、他の型で62進数を扱うとあっという間にオーバーフローしてしまうからです(^_^;)

※使い道
仕事で数字とアルファベットを使用した36進数ってのは使った事がありますね。
データの簡単な暗号化に使うくらいしか思いつかないですね。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* unsigned long long型のべき乗計算 */
unsigned long long pow_ull(unsigned long long val, unsigned int cnt)
{
    unsigned long long ret = 1;

    for (; cnt > 0; --cnt)
        ret *= val;

    return ret;
}

/* 10進数からN進数へ変換 */
void cvt10ToN(unsigned long long val, const unsigned int base, unsigned int dig, char* str)
{
    const char* parts = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    sprintf(str, "%0*d", dig, 0);
    
    for (; dig > 0; --dig, val /= base)
        if (val != 0)
            str[dig - 1] = parts[val % base];
}

/* 1文字を10進数値へ変換 */
unsigned long long cvtCharTo10(const char ch)
{
    const char* parts = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    unsigned long long i = 0;
    unsigned long long len = (unsigned long long)strlen(parts);

    for (i = 0; i < len; ++i)
        if (ch == *(parts + i))
            return i;

    return 0;
}

/* N進数文字列を10進数値へ変換 */
unsigned long long cvtStrTo10(const char* str, const unsigned int base)
{
    unsigned int i = 0;
    unsigned int dig = (unsigned int)strlen(str);
    unsigned long long ret = 0;

    for (i = 0; i < dig; ++i)
        ret += cvtCharTo10(*(str + i)) * pow_ull(base, dig - i - 1);

    return ret;
}

void main(void)
{
    char str[256];
    unsigned long long i = 0;
    for (i = 0; i < 100000; ++i)
    {
        cvt10ToN(i, 62, 10, str);
        printf("%s = %I64u\n", str, cvtStrTo10(str, 62));
    }
}

Re:文字列

Posted: 2010年8月27日(金) 11:37
by ookami
# 使い道

私は、似たようなロジックを、日時を表す時に使っています。

例えば、2010 8/27 11:31:23 を表すのに、
単に 年月日 時分秒 を結合すると、20100827113123 となりますが、
一見して「日時だなっ」という印象を避けたい場合に、

年(の下2桁),月,日, 時,分,秒 をそれぞれ 0-9a-zA-Z に変換してから結合すると
a8rbvn
となります。

「だからどうした」って感じかもですけど^^;

以下、ソースを載せます。
phpですけど...概念は伝わるかと。


$x60tas="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
function x60ta($s) {
global $x60tas;
return substr($x60tas,intval($s),1);
}
$nocache=date("YmdHis",time());
$nocache=x60ta(substr($nocache, 2,2)).
x60ta(substr($nocache, 4,2)).
x60ta(substr($nocache, 6,2)).
x60ta(substr($nocache, 8,2)).
x60ta(substr($nocache,10,2)).
x60ta(substr($nocache,12,2));

Re:文字列

Posted: 2010年8月27日(金) 11:48
by バグ
>>ookamiさん
私はWeb系のソフトには軽く触った程度ですが、PHPやPerlなんかだと文字列の扱いが楽チンでいいですよね♪

Re:文字列

Posted: 2010年8月27日(金) 12:25
by ookami
ですね!なんだかphpって標準ですごいバリエーション豊かなコマンドがそろってますよね。画像を扱うのとか。zipとか。