给定范围内存在多少个PR编号?

时间:2019-03-25 17:03:37

标签: algorithm

这不是作业问题。我对这个问题很好奇。我的方法是简单的蛮力:-)

我的强力C++代码:

int main()
{
    ll l,r;
    cin>>l>>r;

    ll f=0;
    ll i=l;

    while(i<=r)
    {
        ll j=0;
        string s;
        ll c=0;
        s=to_string(i);

        // cout<<s<<" ";

        ll x=s.length();

        if(x==1)
        {
            c=0;
        }
        else 
        {
            j=0;
            //whil
            while(j<=x-2)
            {
                string b,g;

                b="1";
                g="1";

                b=s[j];
                g=s[j+1];

                ll k1,k2;

                k1=stoi(b);
                k2=stoi(g);

                if(__gcd(k1,k2)==1)
                {
                    c=1;
                    break;
                }

                j++;
            }
        }

        ll d=0;
        j=0;
        while(j<=x-1)
        {
            if( s[j]=='2' || s[j]=='3' || s[j]=='5' || s[j]=='7')
            {
                string b;
                b="1";
                b=s[j];
                ll k1=stoi(b);
                if(i%k1==0)
                {
                    //d=0;
                }
                else
                {
                    d=1;
                    break;
                }
            }
            j++;
        }
        if(c==1 || d==1)
        {
            // cout<<"NO";
        }
        else
        {
            f++;
            // cout<<"PR";
        }
        // cout<<"\n";

        i++;
    }

    cout<<f;

    return 0;
}
  

您将获得2个整数'L'和'R'。您需要查找“ L”至“ R”范围内的所有PR编号的计数。 PR号是满足以下属性的数字:

     
      
  1. 没有一对相邻的数字是互质的,即PR编号中的相邻数字不会互为质数。

  2.   
  3. PR号可被PR号中以数字形式出现的所有一位素数素数所除。

  4.   
     

注意:如果gcd(a,b)= 1,则两个数字'a'和'b'是互质的。

     

另外,gcd(0,a)= a;

     

示例:
    输入:[2,5]。
    输出:“ 4”。

     

(注意:“ 1”不是素数,尽管很常见)

     

(所有整数:'2','3','4','5')满足PR编号的条件:-)

约束在“ L”,“ R”上:1 <= L, R <= 10^18

解决这个问题的最有效的算法是什么?

3 个答案:

答案 0 :(得分:1)

注意:这只会解决第1部分,即没有一对相邻的数字是互质的,即PR编号中的相邻数字不会互为互质。

这是python中的一种构造方法:与其遍历范围内的所有数字并按条件过滤,我们将构造所有满足条件的数字。请注意,如果我们有一个有效的数字序列,则要使其继续有效,只有最右边的数字才有意义,以便确定下一个数字是什么。

def ways(max_number, prev_digit, current_number):
    if current_number > max_number:
        return 0
    count = 1
    if prev_digit == 0:
        if current_number != 0:
            count += ways(max_number, 0, current_number * 10)
        for i in range(2, 10): 
            count += ways(max_number, i, current_number * 10 + i)
    if prev_digit == 2 or prev_digit == 4 or prev_digit == 8:
        for i in [0, 2, 4, 6, 8]:
            count += ways(max_number, i, current_number * 10 + i)
    if prev_digit == 3 or prev_digit == 9:
        for i in [0, 3, 6, 9]:
            count += ways(max_number, i, current_number * 10 + i)
    if prev_digit == 5 or prev_digit == 7:
        count += ways(max_number, 0, current_number * 10)
        count += ways(max_number, prev_digit, current_number * 10 + prev_digit)
    if prev_digit == 6:
        for i in [0, 2, 3, 4, 6, 8, 9]:
            count += ways(max_number, i, current_number * 10 + i)
    return count

由于我们正在生成所有最大数目的有效数字,而没有任何重复,因此该函数的复杂度为O(0到max_number之间满足条件1的数目)。要计算从a到b的范围,我们只需要执行ways(b) - ways(a - 1)

花费不到1秒的时间即可将这些数字计算为0到1百万,因为只有42935个数字可以满足结果。由于满足条件的数字很少,因此我们可以检查它们是否是其质数的倍数,以也满足条件2。我将这一部分留给读者,因为有多种实现方法。

答案 1 :(得分:1)

TL; DR:这通常称为“带位掩码的数字动态编程”

用更具竞争性的编程术语来说,您将计算dp[n_digit][mod_2357][is_less_than_r][digit_appeared][last_digit] =具有n_digit位数字(包括前导零)的数字的数量,该数量要少于前n_digit形成的数量R的数字与其他属性匹配。对R和L-1进行两次,然后求差。所需的操作数约为19(数字位数)* 210(修改)* 2 * 2 4 (仅需要检查单位数字质数的出现)* 10 * 10,显然,今天的计算机可以管理它。


考虑如何检查数字是否有效。

不是正常的方法。使用一个有限状态自动机,该输入从左到右逐位输入。

为简单起见,假设输入的位数固定(这样可以更轻松地与L / R进行比较。这是可能的,因为数字最多与R一样多)。

每个州都必须跟踪:

  • 哪个数字出现在数字中(使用位掩码,有4个1位数的质数)
  • 是[L..R]范围内的数字(前缀可以保证该值为true / false,否则前缀与L / R匹配)
  • 每个位数素数的前缀mod的值是什么
  • 最近的数字(以检查所有连续数字对是否都是互质的)

构造有限状态自动机后,其余的操作很简单。只需使用动态编程就可以计算从起始状态到任何接受状态的路径数。


备注:此方法可用于计算可以使用有限状态自动机验证的任何类型的对象的数量(大致来说,您可以使用内存使用率恒定的程序来检查是否满足该属性,并且一个对象一个接一个地

答案 2 :(得分:0)

我们需要一个表,在其中可以查找与前缀匹配以构造有效数字的后缀计数。给定前缀

right digit
prime combination
mod combination

和后缀长度,我们想要可搜索的后缀数:

left digit
length
prime combination
mod combination

我开始使用Python进行编码,然后切换到JavaScript以提供摘要。代码中的注释描述了每个查找表。其中有一些可以加快枚举速度。有一些前缀后缀计算示例,以说明如何使用该表构建任意上限,尽管至少有一些可能在制表过程中进行了所有前缀构建和聚合。

function gcd(a,b){
  if (!b)
    return a
  else
    return gcd(b, a % b)
}

// (Started writing in Python,
// then switched to JavaScript...
// 'xrange(4)' -> [0, 1, 2, 3]
// 'xrange(2, 4)' -> [2, 3]
function xrange(){
  let l = 0
  let r = arguments[1] || arguments[0]
  if (arguments.length > 1)
    l = arguments[0]
  return new Array(r - l).fill(0).map((_, i) => i + l)
}

// A lookup table and its reverse,
// mapping each of the 210 mod combinations,
// [n % 2, n % 3, n % 5, n % 7], to a key
// from 0 to 209.
// 'mod_combs[0]' -> [0, 0, 0, 0]
// 'mod_combs[209]' -> [1, 2, 4, 6]
// 'mod_keys[[0,0,0,0]]' -> 0
// 'mod_keys[[1,2,4,6]]' -> 209
let mod_combs = {}
let mod_keys = {}
let mod_key_count = 0
for (let m2 of xrange(2)){
  for (let m3 of xrange(3)){
    for (let m5 of xrange(5)){
      for (let m7 of xrange(7)){
        mod_keys[[m2, m3, m5, m7]] = mod_key_count
        mod_combs[mod_key_count] = [m2, m3, m5, m7]
        mod_key_count += 1
      }
    }
  }
}

// The main lookup table built using the
// dynamic program
// [mod_key 210][l_digit 10][suffix length 20][prime_comb 16]
let table = new Array(210)
for (let mk of xrange(210)){
  table[mk] = new Array(10)
  for (let l_digit of xrange(10)){
    table[mk][l_digit] = new Array(20)
    for (let sl of xrange(20)){
      table[mk][l_digit][sl] = new Array(16).fill(0)
    }
  }
}

// We build prime combinations from 0 (no primes) to
// 15 (all four primes), using a bitmask of up to four bits.
let prime_set = [0, 0, 1<<0, 1<<1, 0, 1<<2, 0, 1<<3, 0, 0]

// The possible digits that could
// follow a digit
function get_valid_digits(digit){
  if (digit == 0)
    return [0, 2, 3, 4, 5, 6, 7, 8, 9]
  else if ([2, 4, 8].includes(digit))
    return [0, 2, 4, 6, 8]
  else if ([3, 9].includes(digit))
    return [0, 3, 6, 9]
  else if (digit == 6)
    return [0, 2, 3, 4, 6, 8, 9]
  else if (digit == 5)
    return [0, 5]
  else if (digit == 7)
    return [0, 7]
}

// Build the table bottom-up

// Single digits
for (let i of xrange(10)){
  let mod_key = mod_keys[[i % 2, i % 3, i % 5, i % 7]]
  let length = 1
  let l_digit = i
  let prime_comb = prime_set[i]
  table[mod_key][l_digit][length][prime_comb] = 1
}

// Everything else
// For demonstration, we just table up to 6 digits
// since either JavaScript, this program, or both seem
// to be too slow for a full demo.
for (let length of xrange(2, 6)){
  // We're appending a new left digit
  for (let new_l_digit of xrange(0, 10)){
    // The digit 1 is never valid
    if (new_l_digit == 1)
      continue

    // The possible digits that could
    // be to the right of our new left digit      
    let ds = get_valid_digits(new_l_digit)

    // For each possible digit to the right
    // of our new left digit, iterate over all
    // the combinations of primes and remainder combinations.
    // The ones that are populated are valid paths, the
    // sum of which can be aggregated for each resulting
    // new combination of primes and remainders.
    for (let l_digit of ds){
      for (let p_comb of xrange(16)){
        for (let m_key of xrange(210)){
          new_prime_comb = prime_set[new_l_digit] | p_comb
          // suffix's remainder combination
          let [m2, m3, m5, m7] = mod_combs[m_key]
          // new remainder combination
          let m = Math.pow(10, length - 1) * new_l_digit
          let new_mod_key = mod_keys[[(m + m2) % 2, (m + m3) % 3, (m + m5) % 5, (m + m7) % 7]]

          // Aggregate any populated entries into the new
          // table entry
          table[new_mod_key][new_l_digit][length][new_prime_comb] += table[m_key][l_digit][length - 1][p_comb]
        }
      }
    }
  }
}


// If we need only a subset of the mods set to
// zero, we need to check all instances where
// this subset is zero. For example,
// for the prime combination, [2, 3], we need to
// check all mod combinations where the first two
// are zero since we don't care about the remainders
// for 5 and 7: [0,0,0,0], [0,0,0,1],... [0,0,4,6]
// Return all needed combinations given some
// predetermined, indexed remainders.
function prime_comb_to_mod_keys(remainders){
  let mod_map = [2, 3, 5, 7]
  let mods = []
  for (let i of xrange(4))
    mods.push(!remainders.hasOwnProperty(i) ? mod_map[i] - 1 : 0)
  
  function f(ms, i){
    if (i == ms.length){
      for (let idx in remainders)
        ms[idx] = remainders[idx]
      return [mod_keys[ms]]
    }    
    let result = []
    for (let m=ms[i] - 1; m>=0; m--){
      let _ms = ms.slice()
      _ms[i] = m
      result = result.concat(f(_ms, i + 1))
    }
    return result.concat(f(ms, i + 1))
  }
  return f(mods, 0)
}

function get_matching_mods(prefix, len_suffix, prime_comb){
  let ps = [2, 3, 5, 7]
  let actual_prefix = Math.pow(10, len_suffix) * prefix
  let remainders = {}
  for (let i in xrange(4)){
    if (prime_comb & (1 << i))
      remainders[i] = (ps[i] - (actual_prefix % ps[i])) % ps[i]
  }
  return prime_comb_to_mod_keys(remainders)
}

// A brute-force function to check the
// table is working. Returns a list of
// valid numbers of 'length' digits
// given a prefix.
function confirm(prefix, length){
  let result = [0, []]
  let ps = [0, 0, 2, 3, 0, 5, 0, 7, 0, 0]
  let p_len = String(prefix).length

  function check(suffix){
    let num = Math.pow(10, length - p_len) * prefix + suffix
    let temp = num
    prev = 0
    while (temp){
      let d = temp % 10
      if (d == 1 || gcd(prev, d) == 1 || (ps[d] && num % d))
        return [0, []]
      prev = d
      temp = ~~(temp / 10)
    }
    return [1, [num]]
  }

  for (suffix of xrange(Math.pow(10, length - p_len))){
    let [a, b] = check(suffix)
    result[0] += a
    result[1] = result[1].concat(b)
  }
  return result
}

function get_prime_comb(prefix){
  let prime_comb = 0
  while (prefix){
    let d = prefix % 10
    prime_comb |= prime_set[d]
    prefix = ~~(prefix / 10)
  }
  return prime_comb
}

// A function to test the table
// against the brute-force method.
// To match a prefix with the number
// of valid suffixes of a chosen length
// in the table, we want to aggregate all
// prime combinations for all valid digits,
// where the remainders for each combined
// prime combination (prefix with suffix)
// sum to zero (with the appropriate mod).
function test(prefix, length, show=false){
  let r_digit = prefix % 10
  let len_suffix = length - String(prefix).length
  let prefix_prime_comb = get_prime_comb(prefix)

  let ds = get_valid_digits(r_digit)
  let count = 0

  for (let l_digit of ds){
    for (let prime_comb of xrange(16)){
      for (let i of get_matching_mods(prefix, len_suffix, prefix_prime_comb | prime_comb)){
        let v = table[i][l_digit][len_suffix][prime_comb]
        count += v
      }
    }
  }

  let c = confirm(prefix, length)
  
  return `${ count }, ${ c[0] }${ show ? ': ' + c[1] : '' }`
}

// Arbitrary prefixes
for (let length of [3, 4]){
  for (let prefix of [2, 30]){
    console.log(`prefix, length: ${ prefix }, ${ length }`)
    console.log(`tabled, brute-force: ${ test(prefix, length, true) }\n\n`)
  }
}

let length = 6
for (let l_digit=2; l_digit<10; l_digit++){
  console.log(`prefix, length: ${ l_digit }, ${ length }`)
  console.log(`tabled, brute-force: ${ test(l_digit, length) }\n\n`)
}