我怎么能让这个代码kata解决方案运行得更快?

时间:2016-10-19 15:02:10

标签: ruby

我最近参加了一些kata练习,今天早上我解决了以下难题。这里是从Codewars

复制的
You have to create a function that takes a positive integer number and returns the next bigger number formed by the same digits:

next_bigger(12)==21
next_bigger(513)==531
next_bigger(2017)==2071

If no bigger number can be composed using those digits, return -1:

next_bigger(9)==-1
next_bigger(111)==-1
next_bigger(531)==-1

def next_bigger(n)
  root_number = n
  num_material = n.to_s.split(//)
  list_of_nums = num_material.permutation(num_material.size).to_a
  possible_combinations = []
  list_of_nums.each do |n|
    num = n.join.to_i
    possible_combinations << num if root_number < num 
  end
  possible_combinations.empty? ? -1 : possible_combinations.min
end

代码解决了kata,但是我注意到当我尝试运行整个测试套件时,我总是遇到Request Timeout错误。看起来我的解决方案太慢了。我怎么可能让这更快?蝙蝠,我认为递归可能是一个更快的实现,但一点点的谷歌搜索显示它不一定有效率的语言,如python,ruby,java等。我有什么选择让这更快?

2 个答案:

答案 0 :(得分:1)

您应该将问题分解为子步骤。

首先,查看是否有任何方法可以在不修改其他数字的情况下增加个位数。如果可能的话,它肯定会产生答案,因为增加1位数总是产生比增加更大数字(例如,数十位)更小的数字。但是,在不减少其他数字的情况下,不可能增加个位数,因为新数字必须是原始数字的字谜。

接下来,查看是否有任何方法可以增加十位数而不修改任何更重要的数字。当且仅当一位数大于十位数时,这是可能的。因此,如果个位数大于十位数,则交换这两位数并将其作为答案返回。

接下来,让我们考虑是否可以在不修改更重要的数字的情况下增加数百位数。如果百位数小于个位数或十位数,那么我们确实可以通过以某种方式置换这三个数字来增加数百位数。我们要么必须用十位数或一位数来交换数百位数。为了确保我们获得尽可能小的数字,我们必须将它与那两个仍然大于百位数的数字中的最低位置交换。然后我们应该按升序排序十位和一位数字,以确保它们确保我们得到最小的数字。

上面的段落非常抽象,所以我应该举一些例子。如果给出9231,我们会重新排列低三位数得到9312.如果我们得到9243,我们会安排低三位数得到9324。

我认为你可以看到这里出现的模式。一般算法是:

以最低有效数字开头循环输入数字的每个数字。列出不太重要的数字的数字值。如果列表包含的值大于我们正在查看的当前数字,那么我们可以通过执行以下操作来获得我们的答案:选择大于我们正在查看的数字的最小值,并将其与我们正在看的数字。 (我们正在修改输入数字。)现在按升序对数字的数字进行排序,这些数字不如我们正在查看的数字那么重要。

输出数字在最后看起来像这样:零个或多个未修改的数字,后跟一个数字,通过交换一个比它重要的数字而增加,然后是一个或多个数字的递增序列。

你可以用Ruby写这个!我建议您尽快将数字拆分为数字数组(例如134变为[1, 3, 4]),并在整个程序中使用这样的数组。

答案 1 :(得分:0)

你好像在线浪费周期:

list_of_nums = num_material.permutation(num_material.size).to_a

想象一下,如果num_material与987654323445678一样大,请考虑代码必须生成的排列数,然后将它们转换为数组以进行进一步处理。我可能只是检查从最小数字和下一个数字开始的数字的程度,继续比较它们直到我找到一个大数字而不是它的下一个最重要的数字(左边的邻居数字),并在我找到之后打破迭代号。

稍后,我们通过将交换的数字标记为pivot并获取枢轴索引来修复已经出现在左侧的大数字。我们通过比较左边大的可能数字来修复它,这个数字大于枢轴但小于到达最小枢轴位置的数字。

我们现在需要再修理一个东西,即通过排序在枢轴右侧改变的数字。

因此,在最坏的情况下,算法将是O(nlogn)[因为我们正在排序]:

number = gets.chomp

number_length = number.length - 1
next_bigger_number_present = false

while !next_bigger_number_present && number_length != 0
  if (number[number_length] > number[number_length-1])
    pivot_number = number[number_length - 1]
    pivot_number_index = number_length - 1

    temp = number[number_length]
    number[number_length] = number[number_length - 1]
    number[number_length - 1] = temp
    next_bigger_number_present = true
  end
  number_length -= 1
end

while next_bigger_number_present && number_length < number.length - 2
  if (pivot_number < number[number_length] && number[pivot_number_index] > number[number_length])
    temp = number[number_length]
    number[number_length] = number[pivot_number_index]
    number[pivot_number_index] = temp
  end
  number_length += 1
end

if next_bigger_number_present
  number[pivot_number_index+1..number.length-1] = number[pivot_number_index+1..number.length-1].split('').sort.join('')
end


puts next_bigger_number_present ? number : -1

我可能会尝试重写代码以使其更具可读性。