4

1) 導入された整数は左から右に昇順でなければならない 2) ゼロはこの規則から除外される.

以下に、3 桁の出力と整数 0、1、2、3、4、5、6、7、8、9 のサブセットを示します。これは全回答のサブセットにすぎません。具体的には、5 で始まるサブセットです。いくつかのメモを含めました。

500  - Zero is used twice
505  - 5 is used twice.  Note that 504 is not included because 5 was introduced on the left  and 4 < 5
506
507
508
509
550
555
556
557
558
559
560
565 - Though 5 < 6, 5 can be used twice because 5 was introduced to the left of 6.
566
567
568
569
570
575
577
578 
579
580
585
588
589
590
595
599

任意の長い出力長 (この例のように 3 だけでなく) に対して実行できる必要があり、特定の整数セットに対して実行できる必要があります。ただし、ゼロは常に、順序付け規則が適用されない整数です。

4

5 に答える 5

0

これはあまり複雑ではないようです。基数 N のインクリメントの改良を書きます。数字が 0 からインクリメントされると、その左側の最大の数字にまっすぐ進むという変更が加えられます。

更新仕様を読み違えたので、これに対する私の最初の取り組みはうまく機能しませんでした。実際のデータセットによってはuniq.sortコストがかかりすぎる場合がありますが、シーケンス内の項目が数桁しかない場合は問題ありません。正しい方法は、2 番目の並べ替えられた数字のコピーを維持することですが、非効率的であることがわかるまで、このままにしておきます。

ここでの 0..N の値は、各桁が取り得る実際の値のソートされたリストへのインデックスとして使用されることを意図していることに注意してください。を呼び出すとmap、シーケンスの実要素が生成されます。

このプログラムは、あなた自身が示したのと同じシーケンスのセクションをダンプします (すべて 5 で始まります)。

def inc!(seq, limit)

  (seq.length-1).downto(0) do |i|

    if seq[i] == limit
      seq[i] = 0
    else
      valid = seq.first(i).uniq.sort
      valid += ((valid.last || 0).next .. limit).to_a
      seq[i] = valid.find { |v| v > seq[i] }
      break
    end
  end

end

seq = Array.new(3,0)

loop do
  puts seq.join if seq[0] == 5
  inc!(seq, 9)
  break if seq == [0,0,0]
end

出力

500
505
506
507
508
509
550
555
556
557
558
559
560
565
566
567
568
569
570
575
577
578
579
580
585
588
589
590
595
599
于 2013-06-15T17:14:33.777 に答える
0

これは C#/疑似コードです。間違いなくコンパイルされません。実装は線形ではありませんが、簡単な最適化を追加して効率を高めることができる場所に注意してください。アルゴリズムは非常に単純ですが、かなり合理的なパフォーマンスのようです (出力に対して線形です。出力は指数関数的に増加すると推測しています... したがって、このアルゴリズムも指数関数的です。しかし、タイトな定数を使用します)。

// Note: I've never used BigInteger before. I don't even know what the
// APIs are. Basically you can use strings but hopefully the arbitrary
// precision arithmetic class/struct would be more efficient. You
// mentioned that you intend to add more than just 10 digits. In
// that case you pretty much have to use a string without rolling out
// your own special class. Perhaps C# has an arbitrary precision arithmetic
// which handles arbitrary base as well?
// Note: We assume that possibleDigits is sorted in increasing order. But you 
// could easily sort. Also we assume that it doesn't contain 0. Again easy fix.
public List<BigInteger> GenSequences(int numDigits, List<int> possibleDigits)
{
    // We have special cases to get rid of things like 000050000...
    // hard to explain, but should be obvious if you look at it
    // carefully
    if (numDigits <= 0) 
    { 
        return new List<BigInteger>(); 
    }

    // Starts with all of the valid 1 digit (except 0)
    var sequences = new Queue<BigInteger>(possibleDigits);

    // Special case if numDigits == 1
    if (numDigits == 1) 
    { 
        sequences.Enqueue(new BigInteger(0)); 
        return sequences; 
    }

    // Now the general case. We have all valid sequences of length 1
    // (except 0 because no valid sequence of length greater than 1
    // will start with 0)
    for (int length = 1; length <= numDigits; length++) 
    {
        // Naming is a bit weird. A 'sequence' is just a BigInteger
        var sequence = sequences.Dequeue(); 
        while (sequence.Length == length) 
        {
            // 0 always works
            var temp = sequence * 10; 
            sequences.Enqueue(temp);

            // Now do all of the other possible last digits
            var largestDigitIndex = FindLargestDigitIndex(sequence, possibleDigits); 
            for (int lastDigitIndex = largestDigitIndex; 
                lastDigitIndex < possibleDigits.Length; 
                lastDigitIndex++)
            {
                    temp = sequence * 10 + possibleDigits[lastDigitIndex]; 
                    sequences.Enqueue(temp);
            }

            sequence = sequences.Dequeue(); 
        } 
    } 
}

// TODO: This is the slow part of the algorithm. Instead, keep track of
// the max digit of a given sequence Meaning 5705 => 7. Keep a 1-to-1
// mapping from sequences to largestDigitsInSequences. That's linear
// overhead in memory and reduces time complexity to linear _with respect to the
// output_. So if the output is like `O(k^n)` where `k` is the number of possible
// digits and `n` is the number of digits in the output sequences, then it's
// exponential
private int FindLargestDigitIndex(BigInteger number, 
    List<int> possibleDigits) 
{
    // Just iterate over the digits of number and find the maximum
    // digit. Then return the index of that digit in the
    // possibleDigits list
}

上記のコメントでアルゴリズムが機能する理由を証明します (ほとんどの場合、少なくとも)。帰納的な議論です。一般的n > 1には、可能なシーケンスを取ることができます。最初のn-1数字 (左から始まる) は、有効な (矛盾する) シーケンスを形成する必要があります。誘導を使用して、最も内側のループのロジックをチェックすると、目的のシーケンスが出力されることがわかります。この特定の実装では、終了などに関するいくつかの証明も必要です。たとえば、 のポイントは、同じに長さのシーケンスを追加している間にQueue、長さのシーケンスを処理したいということです。の順序により、最も内側のループを終了できます (長さのすべてのシーケンスを処理するため)nn+1 QueueQueuewhilenn+1シーケンスに入る前に)。

于 2013-06-15T08:49:20.693 に答える
0

注: 3 つのソリューションが示されています。割れ目を探します。

正しい数字を記述してから、(1..INFINITE).select{|n| valid(n)}.take(1)

それで、何が有効ですか?さて、ここでいくつかの利点を利用しましょう。

class Fixnum
 def to_a
   to_s.split('').collect{|d| d.to_i}
 end
end
123.to_a == [1,2,3]

各桁は、すでに存在する桁またはゼロ、または前の値より大きい桁であり、最初の桁は常に有効です。

PS -最初の要素を削除したため、ループのインデックスが 's よりも 1 小さいため、inotを使用します。i-1set

def valid num
  #Ignore zeros:
  set = num.to_a.select{|d| d != 0 }

  #First digit is always valid:
  set[1..-1].each_with_index{ |d, i|

    if d > set[i]
      # puts "Increasing digit"

    elsif set[0..i].include? d
      # puts "Repeat digit"

    else
      # puts "Digit does not pass"
      return false
    end
  }
  return true
end

それでは、怠け者の万歳:

  (1..Float::INFINITY).lazy.select{|n| valid n}.take(100).force
  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24,
  # 25, 26, 27, 28, 29, 30, 33, 34, 35, 36, 37, 38, 39, 40, 44, 45, 46, 47, 48, 49, 50, 55,
  # 56, 57, 58, 59, 60, 66, 67, 68, 69, 70, 77, 78, 79, 80, 88, 89, 90, 99, 100, 101, 102, 
  # 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 
  # 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 133, 134, 135, 136]

それができたので、簡潔にしましょう。

def valid2 num
  set = num.to_a.select{|d| d != 0 }
  set[1..-1].each_with_index{ |d, i|
      return false unless (d > set[i]) || (set[0..(i)].include? d)
  }
  return true
end

小切手:

(1..Float::INFINITY).lazy.select{|n| valid n}.take(100).force - (1..Float::INFINITY).lazy.select{|n| valid2 n}.take(100).force
#=> []

今すぐ一緒に:

def valid num
  set = num.to_s.split('').collect{|d| d.to_i}.select{|d| d != 0 }
  set[1..-1].each_with_index{ |d, i|
      return false unless (d > set[i]) || (set[0..(i)].include? d)
  }
  return true
end

編集: セットの特定のサブセットが必要な場合は、範囲を変更するだけです。あなたのオリジナルは次のようになります。

(500..1000).select{|n| valid n}

Edit2: 指定された桁数の範囲を生成するにはn:

((Array.new(n-1, 0).unshift(1).join('').to_i)..(Array.new(n, 0).unshift(1).join('').to_i))

Edit3: 興味深い代替方法 - 数字が有効になると再帰的に削除します。

def _rvalid set
  return true if set.size < 2
  return false if set[1] < set[0]
  return _rvalid set.select{|d| d != set[0]}
end

def rvalid num
  return _rvalid num.to_s.split('').collect{|d| d.to_i}.select{|d| d != 0 }
end

(1..Float::INFINITY).lazy.select{|n| rvalid n}.take(100).force

編集 4: 正の生成方法

def _rgen set, target
  return set if set.size == target
  ((set.max..9).to_a + set.uniq).collect{ |d| 
    _rgen((set + [d]), target)
  }
end

def rgen target
  sets = (0..9).collect{|d|
    _rgen [d], target
  }

  # This method has an array problem that I'm not going to figure out right now
  while sets.first.is_a? Array
    sets = sets.flatten
  end
  sets.each_slice(target).to_a.collect{|set| set.join('').to_i}
end
于 2013-06-15T08:09:31.803 に答える