C言語関係掲示板

過去ログ

No.1325 虫食い覆面算で重複文字を除外

[戻る] [ホームページ]
No.17779

重複文字の除外について
投稿者---清清(2004/11/04 18:03:55)


今、虫食い覆面算を作っています。重複文字を除外しなければ同じ文字があってもその中の数が違う数同士になってしまいます。それを防ぐ為に重複文字を除外したいのですが何かいい方法はありませんか???
入力は3個の文字列を用意して、1つの配列の中に入れ重複文字を判別して同じ数字が入るようにしたいのです!


No.17804

Re:重複文字の除外について
投稿者---たいちう(2004/11/05 09:27:44)


> 今、虫食い覆面算を作っています。

作っているのは、虫食い覆面算の問題ですか?
それとも、既にある問題を解くためのプログラムですか?

後者だったら説明するよりもプログラムを作ったほうが100倍早いので、
問題をここに載せてもらえば、私なりのプログラムを作れます。
よほどとんでもない問題でなければ大丈夫でしょう。
CかC++かで大きく変わるので、指定してください。

# 面白い問題なら他の人も参加してくれそう。

前者だったらもっと具体的に説明してください。
この説明であなたがどこでつまづいているか判る人は少ないと思います。
書きかけのプログラムを載せたりするのも吉。


No.17805

Re:重複文字の除外について
投稿者---たいちう(2004/11/05 09:41:36)


書き忘れましたが、あなたの楽しみを奪うつもりはないので、
プログラムは不要で少しのアドバイスだけが欲しい場合はその旨書いてください。
その場合でも問題を載せてくれたほうがより良いアドバイスができると思います。


No.17806

Re:重複文字の除外について
投稿者---清清(2004/11/05 10:06:23)


どんな入力にも対応し、解をだす虫食い覆面算を組んでいます。
どんなと言っても、やはり限度があるので条件として
1)足し算限定
2)A+B=C の形
3)桁は10桁まで
で作成しています。
組みあがった時の目標として、解がひとつしか無い完全な虫食い覆面算を探し出す事です。

私はどんな入力にも対応する虫食い算を作成し、それに覆面算を組み合わせて作ろうとしてます。
現段階では桁上がりを考慮しない 一桁+一桁=一桁
の虫食い算はできました。
今は桁上がりを考慮した虫食い算で悩んでいます。
何か参考になるようなアドバイスお願いします。

*虫食いの部分はスペースで入力するため、getsを使用しています。
*使用言語はc++です


No.17808

Re:重複文字の除外について
投稿者---たいちう(2004/11/05 11:57:37)


アドバイスにならないかもしれませんが、、、

覆面算から発展させたほうが良くないですか?
どんな入力にも対応できる覆面算ソルバーを作り、
虫食いの部分をワイルドカードのように扱う。

1.問題文字列の入力
2.問題の構造解析
3.順列の発生
4.答えのチェックと表示

1は問題ないでしょうし、3もC++ならnext_permutationが便利です。

2が面倒ですね。2ができたら4は自動的に。

そのうちに作ろうと前々から思っていたので近日中に挑戦してみます。
きっと作れると思うけど私では泥臭いものになりそう。

# STLを駆使した洗練されたソースも見たいな。


No.17810

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 13:04:12)


># STLを駆使した洗練されたソースも見たいな。

ちっとも洗練されていませんが、ご参考。
SEND + MORE = MONEY を解く。

#include <iostream>
#include <algorithm>
#include <string>
#include <set>

int number(const std::string& key, const std::string& permutation) {
  int result = 0;
  for ( int i = 0; i < key.size(); ++i ) {
    result = result * 10 + permutation.find(key[i]);
  }
  return result;
}

std::string digits(const std::string& key, const std::string& permutation) {
  std::string result;
  for ( int i = 0; i < key.size(); ++i ) {
    result += (permutation.find(key[i]) + '0');
  }
  return result;
}

int main() {
  const std::string A = "SEND";
  const std::string B = "MORE";
  const std::string C = "MONEY";
  std::set<char> pset;
  pset.insert(A.begin(), A.end());
  pset.insert(B.begin(), B.end());
  pset.insert(C.begin(), C.end());
  if ( pset.size() > 10 ) {
    std::cerr << "Too many digits!" << std::endl;
    return -1;
  }
  std::string unique(pset.begin(), pset.end());
  std::string permutation = unique;
  std::set<std::string> answers;
  while ( permutation.size() != 10 ) permutation += ' ';
  do {
    int na = number(A, permutation);
    int nb = number(B, permutation);
    int nc = number(C, permutation);
    if ( na + nb == nc ) {
      if ( answers.insert(digits(unique, permutation)).second ) {
        std::cout << digits(A, permutation) << " + "
                  << digits(B, permutation) << " = "
                  << digits(C, permutation) << std::endl;
      }
    }
  } while ( next_permutation(permutation.begin(), permutation.end()) );
  return 0;
}





No.17811

Re:重複文字の除外について
投稿者---Hermit(2004/11/05 13:10:22)


最初の文字は、ゼロでもいいの?


No.17812

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 13:20:15)


>最初の文字は、ゼロでもいいの?

ふつーダメでしょうね。
# そいつの除外はif文イッパツですから。

if ( number("S",permutation) * number("M",permutation) == 0 ) continue;



No.17813

Re:重複文字の除外について
投稿者---Hermit(2004/11/05 13:24:57)


そうですね(^^;


No.17814

Re:重複文字の除外について
投稿者---たいちう(2004/11/05 13:29:05)


書いてくれるかもと期待はしていたのですが、こんなに早くとは。感謝です。
いまから出かけるので、帰ってから勉強させていただきます。


No.17815

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 14:02:43)


>いまから出かけるので、帰ってから勉強させていただきます。

出かけている隙に改版。ちょっとソレっぽく。

#include <iostream>
#include <algorithm>
#include <string>
#include <set>
#include <cassert>

class permutation {
private:
  std::string permutation_;
  std::string unique_;
public:
  typedef std::pair<int,std::string> pair_type;
  explicit permutation(const std::string& keywords) {
    std::set<char> unique(keywords.begin(), keywords.end());
    assert( unique.size() <= 10 );
    permutation_.assign(unique.begin(),unique.end());
    unique_ = permutation_;
    while ( permutation_.size() != 10 ) {
      permutation_ += ' ';
    }
  }
  pair_type pair(const std::string& key) const {
    pair_type result(0,"");
    for ( int i = 0; i < key.size(); ++i ) {
      int n = permutation_.find(key[i]);
      result.first = result.first * 10 + n;
      result.second += (n + '0');
    }
    return result;
  }
  std::string hash() const {
    return pair(unique_).second;
  }
  bool next() {
    return next_permutation(permutation_.begin(), permutation_.end());
  }
};

int main() {
  const std::string A = "SEND";
  const std::string B = "MORE";
  const std::string C = "MONEY";

  permutation perm(A+B+C);
  std::set<std::string> answers;
  do {
    permutation::pair_type na = perm.pair(A);
    permutation::pair_type nb = perm.pair(B);
    permutation::pair_type nc = perm.pair(C);
    if ( na.first + nb.first == nc.first ) {
      if ( answers.insert(perm.hash()).second ) {
        std::cout << na.second << " + "
                  << nb.second << " = "
                  << nc.second << std::endl;
      }
    }
  } while ( perm.next() );
  return 0;
}





No.17816

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 15:19:13)


いたたた、チョンボだ。

next_permutation の前の std:: が抜けてます。陳謝 _o/L



No.17823

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 16:12:19)


さらにチョンボ orz

permutation::permutation の中:

while ( permutation_.size() != 10 ) {
&nbsp; permutation_.insert(permutation_.begin(), ' ');
}

がせーかい。
# かずまさんのツッコミに感謝。




No.17818

Re:重複文字の除外について
投稿者---かずま(2004/11/05 15:27:26)


> ちっとも洗練されていませんが、ご参考。
> SEND + MORE = MONEY を解く。

C で書くとこうなるということですね。ちょっと手抜きですが。
#include <stdio.h>
#include <string.h>

int perm(char *s, int n)
{
    int i = n, j, k, t;
    while (--i > 0 && s[i-1] >= s[i]) ;
    if (i <= 0) return 0;
    for (k = i, j = n; i < --j; i++) t = s[i], s[i] = s[j], s[j] = t;
    for (t = s[k-1], i = k; s[i] <= t; i++) ;
    s[k-1] = s[i], s[i] = t;
    return 1;
}

int val(const char *s, const char *p)
{
    int n = 0;
    while (*p) n = n * 10 + strchr(s, *p++) - s;
    return n;
}

int main(void)
{
    char s[] = "DEMNORSY--";
    do {
        int a = val(s, "SEND"), b =val(s, "MORE"), c =val(s, "MONEY");
        if (a + b == c) printf("%04d + %04d = %05d\n", a, b, c);
    } while (perm(s, 10));
    return 0;
}



No.17819

Re:重複文字の除外について
投稿者---かずま(2004/11/05 15:42:27)


>   while (*p) n = n * 10 + strchr(s, *p++) - s;

訂正
    while (*p) n = n * 10 + (strchr(s, *p++) - s);



No.17820

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 15:43:31)


>> ちっとも洗練されていませんが、ご参考。
>> SEND + MORE = MONEY を解く。
>
>C で書くとこうなるということですね。ちょっと手抜きですが。
>...

ないす。ただし重複解が出ちゃうかな。



No.17831

Re:重複文字の除外について
投稿者---かずま(2004/11/06 15:19:52)


> ないす。ただし重複解が出ちゃうかな。

どんな場合に出るのでしょうか?

ABC の順列は、ABC, ACB, BAC, BCA, CAB, CBA です。
ABB の順列は、ABB, ABB, BAB, BBA, BAB, BBA ではなく、ABB, BAB, BBA です。

perm (next_pernumtationも同じ) は、ABB から、ABB, BAB, BBA を生成します。

したがって、"--DEMNORSY" のように同じ文字があっても重複解が出るとは
思えないんですが、いかがでしょうか。


No.17835

Re:重複文字の除外について
投稿者---επιστημη(2004/11/06 20:40:05)


>したがって、"--DEMNORSY" のように同じ文字があっても重複解が出るとは
>思えないんですが、いかがでしょうか。

えと、-- のトコが 01 でも 10 でも解に変わりはないわけで、
てことは同じ解の候補がふたつ出るわけで、それで。

勘違いならごめんなさいです。



No.17821

Re:重複文字の除外について
投稿者---かずま(2004/11/05 16:01:10)


>   char s[] = "DEMNORSY--";

訂正
    char s[] = "--DEMNORSY";

こうしないと、A + B = C が解けません。

επιστημηさんのも、' ' を前につけるか、あるいは
後ろにつけたいのなら 'Z' より大きいものにしないとだめでしょう。



No.17822

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 16:10:13)


> επιστημηさんのも、' ' を前につけるか、あるいは
> 後ろにつけたいのなら 'Z' より大きいものにしないとだめでしょう。

あ、あ、あ orz そかそか。


No.17824

Re:重複文字の除外について
投稿者---たいちう(2004/11/05 16:18:10)


やはり私の消化能力以上のスピードで進んでますね。MLも。
今私にできることは、当初の質問に近づけるべく
こんなページを見つけることくらい。ググッただけですが。

http://www015.upp.so-net.ne.jp/hara/menu_10.html

虫食いが入ると解の絞込みが難しくなるので、
足し算の覆面虫食い算は少ないのではないかと。
完全虫食い算も割り算が多いし。
後でニコリでも漁ってみます。


No.17825

Re:重複文字の除外について
投稿者---επιστημη(2004/11/05 16:29:25)


>やはり私の消化能力以上のスピードで進んでますね。MLも。

スレ主を'置いてけ堀'にしちゃったかしら。ごめんなさいです。



No.17826

Re:重複文字の除外について
投稿者---かずま(2004/11/05 18:10:19)


> スレ主を'置いてけ堀'にしちゃったかしら。ごめんなさいです。

私が自分のプログラムを手抜きだといったのは、重複文字の除去がないからでした。

重複文字の除去は、次のプログラムの append() の中でやっています。
最上位桁のゼロを除く処理も追加しました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int perm(char *s, int n)
{
    int i = n, j, k, t;
    while (--i > 0 && s[i-1] >= s[i]) ;
    for (k = i, j = n; i < --j; i++) t = s[i], s[i] = s[j], s[j] = t;
    if (k <= 0) return 0;
    for (t = s[k-1], i = k; s[i] <= t; i++) ;
    s[k-1] = s[i], s[i] = t;
    return 1;
}

int val(const char *s, const char *p)
{
    int n = 0;
    while (*p) n = n * 10 + (strchr(s, *p++) - s);
    return n;
}

int append(char *s, int n, const char *p)
{
    for (; *p; p++)
        if (!strchr(s, *p)) {
            if (n >= 10) exit(1);
            s[n++] = *p;
        }
    return n;
}

int comp(const void *a, const void *b)
{
    return *(char *)a - *(char *)b;
}

int main(void)
{
    char x[256], y[256], z[256], s[11] = "";  int n, a, b, c;

    if (!gets(x) || !gets(y) || !gets(z)) return 1;
    n = append(s, 0, x), n = append(s, n, y), n = append(s, n, z);
    while (n < 10) s[n++] = ' ';
    qsort(s, 10, 1, comp);
    do {
        a = val(s, x), b = val(s, y), c = val(s, z);
        if (a + b == c && *x != *s && *y != *s && *z != *s)
            printf("%d + %d = %d\n", a, b, c);
    } while (perm(s, 10));
    return 0;
}



No.17832

Re:重複文字の除外について
投稿者---清清(2004/11/06 15:38:40)


>> スレ主を'置いてけ堀'にしちゃったかしら。ごめんなさいです。

こちらこそアドバイス仰いでおいて遅レス申し訳ございません。
私はプログラムの知識はまだまだ初心者レベルなのでまずみなさまに作って
いただいたプログラムの理解からはじめたいと思います。
素人が口をだすのもナンセンスだと思いますので、しばらく上記のことをし
つつ、私はなるべくROM進行で作成させていきたいと思います。
真に勝手ながら、また自分で攻略できそうにない事がでたら質問させていた
だきたいと思います。


No.17834

Re:重複文字の除外について
投稿者---Hermit(2004/11/06 19:43:04)


結構みんな面白がってやってるみたいだから
口出した方がありがたいんじゃないかな(^^;


No.17849

Re:重複文字の除外について
投稿者---επιστημη(2004/11/08 10:05:49)


>素人が口をだすのもナンセンスだと思いますので、しばらく上記のことをし
>つつ、私はなるべくROM進行で作成させていきたいと思います。

ごめんなさいね。黙ります。
スレ主がついてこれないんじゃ意味がないし。