文字列の数、各文字が偶数回出現する必要がある場合


9

私はしばらくの間、この問題に頭蓋骨をぶつけてきましたが、それは本当に私を苛立たせ始めています。問題は:

私は、文字のセットを持ってABC、とD。長さがnあり、各文字が偶数回出現する必要がある場合、これらの文字から文字列を構築する方法をいくつも教えなければなりません。

たとえば、の答えn = 2は4です。

AA
BB
CC
DD

の答えn = 4は40です。これらの有効な文字列の一部は次のとおりです。

AAAA
AABB
CACA
DAAD
BCCB

私は論理を思いつくのに行き詰まっています。このためのDPソリューションがあると思います。これを介して私の方法を総当たり的にすることは問題外です:ソリューションの数は急速に膨大な数に成長します。

私はあらゆる種類のアイデアを紙に描いてみましたが、役に立ちませんでした。それらのほとんどすべては、それらの複雑さが大きすぎるために破棄しなければなりませんでした。ソリューションは、に対して効率的である必要がありますn = 10^4

私のアイデアの1つは、実際の文字列を追跡するのではなく、各文字が偶数回または奇数回出現したかどうかを追跡することでした。このロジックを適用する方法を思いつきませんでした。

誰か助けてもらえますか?


3
文字列を列挙したり、文字列の数を計算したりする必要がありますか?文字列の数だけが必要な場合は、組み合わせ論を使用して直接数量を計算できます。

@Snowman必要な文字列の数だけが必要です。ただし、ここで組み合わせ論を使用できる可能性は低いと思います。たとえ方法があったとしても、問題は純粋な数学で解決されるべきではない確信しているので、そのためにそうしたくないのです。またはどういう意味ですか?
Olavi Mustanoja、2015

2
もちろん、組み合わせ論を使うことができます。長さNの文字列の場合、{AA、BB、CC、DD}のすべての組み合わせを取得します。各組み合わせについて、一意の順列を取得します。次に、各組み合わせの結果を組み合わせて、一意の順列の1つのセットにします。主に一意性の制約のため、これを行う方法はわかりませんが、方法があると確信しています。

@スノーマン私はあなたが何を意味するかを参照してください。しかし、少なくともその組み合わせを保存する必要はないでしょうか?一意の順列の数を取得するにはこれが必要であり、組み合わせの数は非常に急速に増えて、格納できない可能性のある比率になります。
オラビムスタノハ2015

1
たぶん。私は確かに知っている組み合わせ論に十分に精通していません。たぶんMathematics.SEはこれに似た質問がありますか?今はそれを掘り下げる時間はありませんが、これは興味深い問題です。それについて考えて、またチェックします。

回答:


5

異なる文字を使用して(つまりf(n,d))長さの(偶数)の順列の数を与える関数として設定します。ndd=4

明らかにf(0,d) = 1f(n,1) = 1あなたがたった1つの文字、またはゼロのスペースを持っているとき、1つの配置しかないので。

次に、導入ステップ:

文字を使用して有効な文字列を作成するには、文字を使用してdより短い偶数長の文字列をd-1取得し、この新しい文字の偶数倍を追加して長さを最大にします。配置の数は、新しい桁にするために文字列の全長から場所choose(n,n_newdigits)を選択できn_newdigit、残りは順番に元の文字列で埋められるためです。

Rで単純な再帰を使用してこれを実装するために、私は次のことを行いました。

f <- function(n,d)
{
  if(n==0) return(1)
  if(d==1) return(1)
  retval=0
  for (i in seq(from=0, to=n, by=2)) retval=retval+f(n-i,d-1)*choose(n,i)
  return(retval)
}

f(4,4)
# 40    

f(500,4)
# 1.339386e+300 takes about 10 secs

興味のある種​​類の数値については、2次元配列に数値を格納し、dの増加に対して繰り返す方が効率的だと思いましたが、これは言語の選択によって異なる場合があります。


4

ミフの答えは間違いなくエレガントです。とにかく私のものをほぼ完成させたので、それを提供します。良いことは、n = 500でも同じ結果が得られることです:-)

許可されている異なる文字の数をdとすると、d = 4になります。

文字列の長さをnとすると、最終的にはnの偶数の値が表示されます。

文字列内のペアになっていない文字の数をuとします。

N(n、d、u)を長さnの文字列の数とし、d個の異なる文字から構築され、u個のペアになっていない文字を持ちます。Nを計算してみましょう。

観察すべきかなりのコーナーケースがあります:

u> dまたはu> n => N = 0

u <0 => N = 0

n%2!= u%2 => N = 0。

nからn + 1にステップするとき、uは1ずつ増加するか、1ずつ減少する必要があるため、次のように再帰します。

N(n、d、u)= f(N(n-1、d、u-1)、N(n-1、d、u + 1))

uを1つ減らす方法はいくつありますか。これは、uのペアになっていない文字の1つをペアにする必要があるため、簡単です。したがって、fの2番目の部分は(u + 1)* N(n-1、d、u + 1)になります。もちろん、u + 1> n-1またはuの場合はN = 0であることに注意する必要があります。 +1> d。

これを理解したら、fの最初の部分が何であるかを簡単に確認できます。u-1個のペアになっていない文字がある場合に、いくつの方法でuを増やすことができますか。さて、ペアになっている(k-(u-1))文字の1つを選択する必要があります。

したがって、すべてのコーナーケースを考慮すると、Nの再帰的な式は次のようになります。

N(n、d、u)=(d-(u-1))* N(n-1、d、u-1)+(u + 1)* N(n-1、d、u + 1)

再帰の解決方法については、http://en.wikipedia.org/wiki/Concrete_Mathematicsで説明するつもりはありません。

代わりに、Javaコードをいくつか作成しました。繰り返しになりますが、Javaは冗長であるため、とにかく少し不格好です。しかし、少なくともJavaでは、スタックが500または1000のネストレベルでオーバーフローしたときに、再帰がはるかに早く中断するため、再帰を使用しないという動機がありました。

n = 500、d = 4、u = 0の結果は次のとおりです。

N(500、4、0)= 133938575898283415118553131132500226320175601463191700930468798546293881390617015311649797351961982265949334114694143353148393160711539255449807219683895854579576904278803546802604812520890471375776580516387245505699580955662718322233732807745959460586868628584025594606426412298521256426464261285862562145862561282512659064256256256125590420に名を付けて(0)N(500、4、0)

中間結果を記憶するため、0.2秒で計算されます。N(40000,4,0)は5秒未満で計算します。ここにもコード:http : //ideone.com/KvB5Jv

import java.math.BigInteger;

public class EvenPairedString2 {
  private final int nChars;  // d above, number of different chars to use
  private int count = 0;
  private Map<Task,BigInteger> memo = new HashMap<>();

  public EvenPairedString2(int nChars) {
    this.nChars = nChars;
  }
  /*+******************************************************************/
  // encodes for a fixed d the task to compute N(strlen,d,unpaired).  
  private static class Task {
    public final int strlen;
    public final int unpaired;

    Task(int strlen, int unpaired) {
      this.strlen = strlen;
      this.unpaired = unpaired;
    }
    @Override
    public int hashCode() {
      return strlen*117 ^ unpaired;
    }
    @Override
    public boolean equals(Object other) {
      if (!(other instanceof Task)) {
        return false;
      }
      Task t2 = (Task)other;
      return strlen==t2.strlen && unpaired==t2.unpaired;
    }
    @Override
    public String toString() {
      return "("+strlen+","+unpaired+")";
    }
  }
  /*+******************************************************************/
  // return corner case or memorized result or null  
  private BigInteger getMemoed(Task t) {
    if (t.strlen==0 || t.unpaired<0 || t.unpaired>t.strlen || t.unpaired>nChars
        || t.strlen%2 != t.unpaired%2) {
      return BigInteger.valueOf(0);
    }

    if (t.strlen==1) {
      return BigInteger.valueOf(nChars);
    }
    return memo.get(t);
  }

  public int getCount() {
    return count;
  }

  public BigInteger computeNDeep(Task t) {
    List<Task> stack = new ArrayList<Task>();
    BigInteger result = null;
    stack.add(t);

    while (stack.size()>0) {
      count += 1;
      t = stack.remove(stack.size()-1);
      result = getMemoed(t);
      if (result!=null) {
        continue;
      }

      Task t1 = new Task(t.strlen-1, t.unpaired+1);
      BigInteger r1 = getMemoed(t1);
      Task t2 = new Task(t.strlen-1, t.unpaired-1);
      BigInteger r2 = getMemoed(t2);
      if (r1==null) {
        stack.add(t);
        stack.add(t1);
        if (r2==null) {
          stack.add(t2);
        }
        continue;
      }
      if (r2==null) {
        stack.add(t);
        stack.add(t2);
        continue;
      }
      result = compute(t1.unpaired, r1, nChars-t2.unpaired, r2);
      memo.put(t,  result);
    }
    return result;
  }
  private BigInteger compute(int u1, BigInteger r1, int u2, BigInteger r2) {
    r1 = r1.multiply(BigInteger.valueOf(u1));
    r2 = r2.multiply(BigInteger.valueOf(u2));
    return r1.add(r2);
  }
  public static void main(String[] argv) {
    int strlen = Integer.parseInt(argv[0]);
    int nChars = Integer.parseInt(argv[1]);

    EvenPairedString2 eps = new EvenPairedString2(nChars);

    BigInteger result = eps.computeNDeep(new Task(strlen, 0));
    System.out.printf("%d: N(%d, %d, 0) = %d%n", 
                      eps.getCount(), strlen, nChars, 
                      result); 
  }
}

2

私は解決策を考え出そうとしましたが、失敗し、Mathematics.StackExchangeで同じ質問をしましたRus Mayのおかげで、Common Lispの解決策は次のとおりです。

(defun solve (n)
  (if (evenp n)
      (/ (+ (expt 4 n) (* 4 (expt 2 n))) 8)
      0))

これは、の奇数値に対して常に0を返しますn。の場合n = 500SBCLを使用した出力は次のとおりです。

* (time (solve 500))

    Evaluation took:
      0.000 seconds of real time
      0.000000 seconds of total run time (0.000000 user, 0.000000 system)
      100.00% CPU
      51,100 processor cycles
      0 bytes consed

    1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.