算法中的无限循环,以匹配以不同速度运行的时钟

时间:2015-09-19 18:06:01

标签: ruby algorithm

我正试图解决这个问题:

  

使用24小时制显示以小时和分钟为单位的时钟的两个时钟正在运行   速度。每个时钟都是每小时的精确分钟数。两个时钟都开始显示相同的时间   (00:00)并按照准确的计时器每小时(一小时后开始)定期检查。   两个时钟在第一次检查并显示同一时间时会显示几点?   注意:对于这个问题,我们只关心检查时的时钟匹配。   例如,假设第一个时钟快速运行1分钟(每小时),第二个时钟运行31分钟   快(每小时)。   •一小时后首次检查时钟时,第一个时钟将显示01:01和第二个时钟   将显示01:31;   •两小时后检查时钟时,它们将显示02:02和03:02;   •48小时后,时钟将显示00:48。

这是我的代码:

def add_delay(min,hash)
    hash[:minutes] = (hash[:minutes] +  min)
    if hash[:minutes] > 59 
        hash[:minutes] %= 60
        if min < 60 
            add_hour(hash)
        end
    end

    hash[:hour] += (min / 60) 
    hash
end

def add_hour(hash) 
    hash[:hour] += 1
    if hash[:hour] > 23
        hash[:hour] %= 24
    end
    hash
end

def compare(hash1,hash2)
    (hash1[:hour] == hash2[:hour]) && (hash1[:minutes] == hash2[:minutes])
end




#-------------------------------------------------------------------
first_clock = Integer(gets) rescue nil
second_clock = Integer(gets) rescue nil



#hash1 = if first_clock < 60 then {:hour => 1,:minutes => first_clock} else {:hour => 1 + (first_clock/60),:minutes => (first_clock%60)} end
#hash2 = if second_clock < 60 then {:hour => 1,:minutes => second_clock} else {:hour => 1 + (second_clock/60),:minutes => (second_clock%60)} end

hash1 = {:hour => 0, :minutes => 0}
hash2 = {:hour => 0, :minutes => 0}

begin 
    hash1 = add_hour(hash1)
    hash1 = add_delay(first_clock,hash1)
    hash2 = add_hour(hash2)
    p hash2.to_s
    hash2 = add_delay(second_clock,hash2)
    p hash2.to_s
end while !compare(hash1,hash2)

#making sure print is good
if hash1[:hour] > 9

    if hash1[:minutes] > 9
        puts hash1[:hour].to_s + ":" + hash1[:minutes].to_s
    else
        puts hash1[:hour].to_s + ":0" + hash1[:minutes].to_s
    end
else
    if hash1[:minutes] > 9
        puts "0" + hash1[:hour].to_s + ":" + hash1[:minutes].to_s
    else
        puts "0" + hash1[:hour].to_s + ":0" + hash1[:minutes].to_s
    end

end

#-------------------------------------------------------------------

对于1和31,代码按预期运行。对于任何更大的东西,比如5和100,它似乎进入一个无限循环,我不知道bug在哪里。出了什么问题?

1 个答案:

答案 0 :(得分:0)

您的代码

让我们看看您的代码,同时做一些小的改进。

将分钟数转换为小时和分钟,然后将小时数转换为一天内的小时数,

add_delay需要一定的分钟数才能添加到散列中。一个问题是,如果时钟每小时增加超过59分钟,则可能需要将小时数增加一个以上。尝试编写它并add_hours像这样:

def add_delay(min_to_add, hash)
  mins = hash[:minutes] + min_to_add
  hrs, mins = mins.divmod 60
  hash[:minutes] = mins
  add_hours(hash, hrs)
end

def add_hours(hash, hours=1)
  hash[:hours] = (hash[:hours] + hours) % 24
end

我们不一定关心这些方法中的任何一个返回,因为它们修改了参数hash

这使用非常方便的方法Fixnum#divmod将分钟转换为小时和分钟。

(旁白:一些Rubie不会使用hash作为变量的名称,因为它也是Ruby方法的名称。)

接下来,compare确定密钥:hour:minutes的两个哈希是否相等。您可以查看哈希是否相等,而不是检查小时和分钟是否匹配:

def compare(hash1, hash2)
  hash1 == hash2
end

获取时钟快速的每小时分钟数:

first_clock  = Integer(gets) rescue nil
second_clock = Integer(gets) rescue nil

现在初始化哈希值并逐步进行,直到找到匹配项,然后返回哈希:

def find_matching_time(first_clock, second_clock)
  hash1 = {:hours => 0, :minutes => 0}
  hash2 = {:hours => 0, :minutes => 0} 
  begin 
    add_delay(first_clock, hash1)
    add_hours(hash1)
    add_delay(second_clock, hash2)
    add_hours(hash2)
  end until compare(hash1, hash2)
  hash1
end

让我们试一试:

find_matching_time(1, 31)
  # => {:hours=>0, :minutes=>48} 
find_matching_time(5, 100)
  #=> {:hours=>0, :minutes=>0}
find_matching_time(5, 5)
  #=> {:hours=>1, :minutes=>5} 
find_matching_time(0, 59)
  #=> {:hours=>0, :minutes=>0} 

这些结果与我在下面获得的结果相匹配。您不会返回从现在起的小时数,直到时间相同,但您可能不需要它。

我还没有确定为什么你会得到无限循环,但也许通过这种分析,你将能够找到它。

我建议另外两个小的变化:1)在add_hours中加入add_delay并重命名后者,以及2)摆脱compare,因为它非常简单且仅使用在一个地方:

def add_hour_and_delay(min_to_add, hash)
  mins = hash[:minutes] + min_to_add
  hrs, mins = mins.divmod 60
  hash[:minutes] = mins
  hash[:hours] = (hash[:hours] + 1 + hrs) % 24
end

def find_matching_time(first_clock, second_clock)
  hash1 = {:hours => 0, :minutes => 0}
  hash2 = {:hours => 0, :minutes => 0} 
  begin 
    add_hour_and_delay(first_clock, hash1)
    add_hour_and_delay(second_clock, hash2)
  end until hash1 == hash2
  hash1
end

替代方法

这是编写方法的另一种方法。让:

  • f0:第一个时钟快速的每小时分钟
  • f1:第二个时钟快速的每小时分钟

然后我们可以计算下次他们将显示如下相同的时间。

<强>代码

MINS_PER_DAY = (24*60)

def find_matching_time(f0, f1)
  elapsed_hours = (1..Float::INFINITY).find { |i|
    (i*(60+f0)) % MINS_PER_DAY == (i*(60+f1)) % MINS_PER_DAY }
  [elapsed_hours, "%d:%02d" % ((elapsed_hours*(60+f0)) % MINS_PER_DAY).divmod(60)]
end

<强>实施例

find_matching_time(1, 31)
  #=> [48, "0:48"]

48小时后,两个时钟将显示&#34; 0:48&#34;的时间。

find_matching_time(5, 100)
  #=> [288, "0:00"]

find_matching_time(5, 5)
  #=> [1, "1:05"] 

find_matching_time(0, 59)
  #=> [1440, "0:00"]

<强>解释

经过i小时后,两个时钟将分别显示一天内的以下分钟数:

(i*(60+f0)) % MINS_PER_DAY # clock 0
(i*(60+f1)) % MINS_PER_DAY # clock 1
然后使用

Enumerable#find确定这两个值相等时的第一个经过时数i。我们不知道可能需要多长时间,因此我列举了以1开头的所有正整数。 (我想它可能不会超过59小时,所以我可以编写(1..n).find.. n是大于58的任何整数。)find返回的值是分配给变量elapsed_hours

两个时钟将在elapsed_hours之后显示相同的时间,因此我们可以计算时钟显示的时间。我已选择为时钟0执行此操作。对于第一个示例(f0=1f1=31

elapsed_hours #=> 48

所以

mins_clock0_advances = elapsed_hours*(60+1)
  #=> 2928

mins_clock_advances_within_day = mins_clock0_advances % MINS_PER_DAY
 #=> 48

然后我们将其转换为小时和分钟:

mins_clock_advances_within_day.divmod(60)
  #=> [0, 48]

然后我们可以使用方法String#%来适当地格式化此结果:

"%d:%02d" % mins_clock_advances_within_day.divmod(60)
  #=> "0:48"

有关使用%时格式化的信息,请参阅Kernel#sprintf。在"%02d"中,d用于&#34;十进制&#34;,2是字段宽度,0表示填充左侧为零。