无意义? 编程问题

无意义? 编程问题
没有意义还是完美? 那是我为这类问题发动的战争。

编程挑战,面试问题和脑筋急转弯的编程。 为了尝试个人成长或深入了解为什么要大量使用这些东西,特别是对于面试问题,我将面临来自hackerrank.com,谷歌搜索甚至您建议中的一些挑战。

首先,让我表达对这些类型问题的一些纯朴的想法。 像这样的编程挑战经常在面试中被用来隐秘地发现某人如何编程。 处理这些类型的问题时,我经常发现自己处于个人挣扎中,大多数时候,我担心它们是没有意义的,在现实世界的编程中没有看到。 当我做这些时,我还发现我对问题的某些方面以及我的编程风格有很多了解。 通常,这些类型的问题也会在白板前面解决, 为此我请埃里克·埃利奥特(Eric Elliot)说,“白板是用于绘图而不是编码。”

对于您要雇用经理的人,不仅仅将这些类型的问题交给白板上的某人并要求他们解决,还建议您如何解决。 我认为透明度最好,而不是隐藏问题中真正要寻找的东西。 “您可以使用算术而不是字符串比较来解决此问题吗?” 或“您能告诉我如何使用正则表达式和模式匹配来解决此问题吗?” 敞开它可能会在您的口中留下一种酸味,因为这可能不是您最终将不得不完成的最终解决方案的方式。 话虽如此,对于被访者在您面前解决问题也很了解,这可能是对问题的快速第一思考,而不是他们在生产环境中处理问题的方式。

无意义? 编程问题
这将是我一天的诊断。

为什么我要重做这些问题? 我做过诸如嗡嗡嗡嗡声,反转数组,查找和替换值之类的问题,然后才浪费时间? 速记答案是我的好奇心。 我不仅要解决问题,还希望进行基准测试并采用测试驱动的方法来解决这些问题。 我不仅要以面试方式挑战自我,还要看哪种解决方案更好,多少更好。 如果将来需要进行各种类型的数学比较,字符串比较或模式匹配,我也将建立一个库,以供将来尝试,我知道在遇到某种类型的问题时可以立即跳出正题。

反元音问题

此问题需要您输入字符串并反转元音。 最初,我是使用JavaScript进行此操作的,但是我并不真正喜欢它,也没有真正的理由继续使用JS (并不是我对JS有问题) ,因此在本文中我将其移至Ruby。 尽管最初的尝试几乎是将JavaScript解决方案移植到Ruby。

第一次尝试

这是我第一次尝试解决该问题,它看起来像是我的JavaScript解决方案的确切移植,但是在Ruby中(这是丑陋的,不是一个很好的解决方案),并且通过在字符串的两端进行工作而变得稍微好一点,而不仅仅是单程。

class ReverseVowels
# the string that gets reversed
attr_accessor :string_to_reverse
  # init method that sets the string to an instance variable
def initialize(string_to_reverse)
@string_to_reverse = string_to_reverse
end

# method that does all the work
def reverse_vowels
# temp arrays
vowels = []
indexes = []

# iterators for while
i = 0
j = string_to_reverse.length - 1
half_way = (j/2.to_f).ceil

# shorten var name temporarily, maybe more in the future
str = string_to_reverse

# iterate stuff
while i <= half_way && j >= half_way
if is_vowel?(str[i])
indexes << i
vowels << str[i]
end

if is_vowel?(str[j])
indexes << j
vowels << str[j]
end

i += 1
j -= 1
end

indexes = indexes.map(&:to_i).sort
vowels.reverse!

vowels.each_with_index do |v,i|
str[indexes[i]] = v
end

return str
end

# checks the vowels except y
def is_vowel?(character)
vowels = ['a', 'e', 'i', 'o', 'u']
vowels.include? character.to_s.downcase
end
end

第二次尝试

这是我显而易见的重构,并尝试删除循环数。 测试没有改变,因此重构很简单且易于测试。 真正清除的唯一方法是反转元音的方法。 通过使用一个在中间相遇并即时进行调整的循环,可以使此过程变得更好的思考过程。

 def reverse_vowels
# iterators for while
i = 0
j = string_to_reverse.length - 1
str = string_to_reverse

# iterate stuff
while i < j
if !is_vowel?(str[i])
i += 1
next
end
if !is_vowel?(str[j])
j -= 1
next
end

str[i], str[j] = str[j], str[i]

i += 1
j -= 1
end

str
end

第三次尝试

迄今为止,这种尝试是最短的,并且不需要使用is_vowel方法。 我希望自己自己解决这个问题,但是在查看ruby api docs中的scan方法时发现了它。 为了弄清楚如何使用扫描方法,实际上出现了反向元音问题。 该解决方案绝对出色, 属于StefanPochmann 扫描方法查找模式,然后将项目转换为数组中的索引项目。 然后,它使用gsub regex模式将数组中的最后一项放入第一个找到的元音中。

def reverse_vowels    
vowels = @string_to_reverse.scan(/[aeiou]/i)
@string_to_reverse.gsub(/[aeiou]/i) { vowels.pop }
end

绩效结果

每个性能测试均经过1000次尝试的迭代,并且每次运行都要进行3次。 运行该对象的3000次尝试来创建每个平均值。

# First Attempt
# Realtime per 1000 attempts
# ----------------------------------
0.02257158898282796
0.011442353017628193
0.010828958998899907
Average Time: 0.01494763366645202
# Second Attempt
# Realtime per 1000 attempts
# ----------------------------------
0.01959512199391611ms
0.00883392000105232ms
0.008858179993694648ms
Average Time: 0.012429073996221026ms
# Third Attempt
# Realtime per 1000 Attempts
# -----------------------------------
0.017268108000280336ms
0.007276029995409772ms
0.007084235025104135ms
Average Time: 0.010542791006931415ms

表演结果很有趣,每个人都可以用不同的方式来解释它们。 纵观全局,我认为这些结果中的任何一个都不会对速度产生显着影响,但最容易维护的结果也是最快的。

总和问题

问题是要取一个大数字,将其每个数字加起来并返回值。 虽然,起初我很简单,但是我完全不了解如何分解这个数字。 对于所有这些我不需要干预的解决方案是将其扩展为数组,然后遍历数字以将其求和。 后来我被告知,数学编程将是更好的方法,并且可以显着提高性能。

第一次尝试

关于非严格类型输入的妙处在于,我可以将其转换为许多不同类型的解决方案。 第一种是使用字符串比较将对象拆分为一个数组,然后将它们加在一起,并在最后返回总和。 它看起来不优雅也不秀气,所以必须进行明确的重构,但是要进行数学重构而不是重构呢?

class Sum
attr_accessor :input
  def initialize(input_num)
@input = input_num
end

def sum_input
digits = @input.to_s.split('')
sum = 0
digits.each do |digit|
sum += digit.to_i
end
sum
end
end

第二次尝试

第二次尝试是我尝试数学上的尝试。 我知道我们的数字系统只有几十个数字,因此我可以将数字除以10来跳出数字,我也知道模运算符会返回余数。 使用模运算符,我能够获得第一个数字的数值,然后除法帮助我从临时迭代器中将其删除。 通过细微的线索可以很容易地找到这种方法,并且当我想到数值问题时,我希望在以后记住和思考。

class Sum
attr_accessor :input
  def initialize(input_num)
@input = input_num
end

def sum_input
i = @input
sum = 0
while i > 0
sum += (i % 10)
i = (i / 10)
end
sum
end
end

第三次尝试

这次尝试试图查看我可以创建多少方法来解决仍通过测试的问题。 在进行此实验之前,有许多4/5衬板,但也有一些实验,但这是我为进行最小尝试而选择的衬板。

class Sum
attr_accessor :input
  def initialize(input_num)
@input = input_num
end

def sum_input
digits = @input.to_s.split('')
sum = eval digits.join '+'
end
end

第三次尝试重构

第三次尝试在性能测试中的评分很差,以至于我多次运行测试,并通过了一些错误检查。 我认为这与第一次尝试是一样的,但是到目前为止是最慢的。 我发现eval是一个缓慢的过程,经过一番挖掘后,我指出了inject方法 ,该方法基本上是在假设数组为整数的情况下对数组的内容求和。 重构之后,第三种方法能够获得与第一种方法相似的性能结果,并通过所有测试。

class Sum
attr_accessor :input

def initialize(input_num)
@input = input_num
end
  def sum_input
digits = @input.to_s.split('').map(&:to_i)
sum = digits.inject(0, :+)
end
end

绩效结果

这些真是大开眼界。 这些测试中的每一个测试都以毫秒为单位,平均进行了3000次以上的平均尝试,并从1到9999999的随机数字进行了平均。数学表达式的速度非常快,不仅达到了我期望的几毫秒。 更糟糕的是,聪明的速记方法

# First Attempt
# Realtime per 1000 attempts
# ----------------------------------
0.007490240968763828
0.006662635016255081
0.0066195380059070885
Average Time: 0.0069241379969753325
# Second Attempt
# Realtime per 1000 attempts
# ----------------------------------
0.0007742019952274859
0.0007528960122726858
0.0007553229806944728
Average Time: 0.0007608069960648814
# Third Attempt
# Realtime per 1000 Attempts
# -----------------------------------
0.01474869396770373
0.014116039033979177
0.01710168702993542
Average Time: 0.015322140010539442
# Third Attempt (Refactor) 
# Realtime per 1000 Attempts
# -----------------------------------
0.007556420983746648
0.00706747395452112
0.006896736973430961
Average Time: 0.007173543970566243

结论

结束这个过程可能是我最难以接受的,因为我已经说过,我认为这些问题毫无意义。 尽管看起来似乎毫无意义,但它们使我思考,学习,并给了我一些在遇到类似问题时可以使用的答案。 如果基于文本,则正则表达式可能被证明是最快,最容易维护的。 如果是数字问题,我应该尝试找出解决问题的数学方法。

TL; DR

我对AF感到好奇,不得不进一步研究一些编码挑战,并且可能会做更多的事情。

From: https://hackernoon.com/pointless-programming-problems-59427ea6f8b6