【问题标题】:divide and conquer: computing the time elapsed分而治之:计算经过的时间
【发布时间】:2015-02-21 10:46:33
【问题描述】:

我必须在我的大学做一个小作业:

我有一个运行“n”个独立服务的服务器。所有这些服务在过去同时启动。并且每个服务 'i' 会在一段时间 's[i]' (以秒为单位)后将 'b[i]' 行写入服务器上的日志文件。输入由“l”日志文件的行数和“n”服务数组成。然后我们在每个服务 i 的下 'n' 行中有:'s[i]' 提到的时间段和 'b[i]' 服务写入日志文件的行数。

我必须根据日志文件中的行数来计算程序都开始运行的时间(以秒为单位)。示例:

input:  
19 3 
7 1
8 1
10 2

Output: 
42

我必须使用分而治之,但我什至无法弄清楚如何将其拆分为子问题。我还必须使用这个函数,其中 ss 是服务周期的数组, bs 是每个服务写入日志文件的行数:

long linesAt(int t, int[] ss, int[] bs) {
  long out = 0;
  for (int i = 0; i < ss.length; i++) {
     // floor operation
     out += bs[i] * (long)(t/ss[i]);
  }
  return out;

ss 和 bs 基本上是输入的数组,如果我们举个例子,它们看起来像这样,上面的行是数组的索引:

ss:

0 1 2
7 8 10

bs:

0 1 2
1 1 2

很容易看出42应该是输出

 linesAt(42) = floor(42/7)*1+floor(42/8)*1+floor(42/10)*2 = 19

现在我要写一个函数

int solve(long l, int[] ss, int[] bs)

我已经用蛮力写了一些伪代码,但我不知道如何用分而治之的范式解决这个问题,我的伪代码如下所示:

Solve(l, ss, bs)
  out = 0
  t = 0
  while (out != l)
    out = linesAt(t, ss, bs)
    t++
  end while
  return t

我认为我必须以某种方式拆分 l,以便计算较小长度的时间。但我真的不明白怎么做,因为当你看这个时,它似乎是不可能的:

t           out
0..6        0
7           1
8           2
9           2
10          4
11..13      4
14          5
15          5
16          6
17..19      6
20          8
...
40          18
42          19

尚塔尔。

【问题讨论】:

  • Chantal,您是否将问题的文本翻译成另一种语言。
  • 不,我没有,为什么?我的英语这么差吗?
  • 不,只是你的名字听起来像法语。我没有完全理解这个问题。
  • @Chantal 我认为您的意思是每个服务在不同的时期后写入不同数量的行。将这些值标记为“b”和“s”是令人困惑的,因为这意味着它们对于每个服务都是相同的。通常人们会写“'b[i]'(其中 i 是服务)”,或者使用另一种表示存在多个值的表示法。
  • @Tarik 你现在明白了吗?

标签: java recursion brute-force divide-and-conquer floor


【解决方案1】:

听起来像经典的二分搜索很合适,但需要先获得一个合适的最大值。您从一些时间 't' 的估计开始(比如 100)并调用 linesAt 以获取该 t 的行。如果返回的值太小(即小于l),则将't'加倍并重试,直到行数太

此时,您的maximumt,而您的minimumt/2。然后你反复:

  • 选择t 作为maximumminimum 之间的中间点
  • 调用linesAt(t,...)获取行数
  • 如果您找到了目标,请停下来。
  • 如果行数过多,调整最大值:maximum = t
  • 如果行数太少,请调整最小值:minimum = t

上述算法是一种二分搜索——它在每次迭代中将搜索空间分成两半。因此,这是一个分而治之的例子。

【讨论】:

  • 我也已经考虑过了。该解决方案与蛮力解决方案一样有效,尽管这绝对比蛮力解决方案要好,但我不确定这是否是最佳和正确的解决方案。因为输入可以变得非常大,基本上你必须先猜测然后开始搜索,这取决于你的猜测有多好,程序需要多长时间才能找到它的解决方案。我的一个测试用例有 l = 6543219876 和 n = 1000000。只是说 t=100 不适用于这种情况(我认为),因为程序需要在至少 5 秒内终止。
  • 以某种方式做出正确的猜测将是使用此算法的关键。
  • 嗯,你可以很容易地计算出平均每秒行数——这将是每个服务每秒平均行数的总和。这可能会让你做出非常接近的猜测。
【解决方案2】:

你正在尝试求解一个整数方程:

 floor(n/7)*1+floor(n/8)*1+floor(n/10)*2 = 19

你可以去掉 floor 函数,求解 n 得到下界和上界,然后在这两个界之间搜索。

求解以下方程:

(n/7)*1+(n/8)*1+(n/10)*2 = 19
n=19/(1/7+1/8+2/10)

找到 n 后,m0 的哪个值范围将是 floor (m0 / 7) = floor (n/7)

floor (n/7) * 7 <= m0 <= (ceiling (n/7) * 7) - 1

以同样的方式,计算m1和m2。

对于介于 1 和 3 之间的 i,取 max (mi) 作为上限,min(mi) 作为下限。

此时进行二分搜索可能有点过头了。

【讨论】:

  • 好主意。在这种情况下,t=40,61,因此下限为 t=41,这实际上已经是一个很好的近似值。所以对于二进制搜索,我将使用我的最小值 41。然后我的最大值为 2*min=82,然后我将在这些值之间使用二进制搜索,这实际上可以工作!我会试一试!谢谢,我会及时通知您。
  • 看起来这个练习的整个想法是让你使用二分搜索。下限,上限的东西只是一个奖金。我认为您可以提出一个较小的下限(通过一些数学分析)。我去看看……
  • 好吧,基本上我搞定了,你对边界的建议非常有效。但是我知道一个特殊情况,输入是:1 2 2147483647 2147483647 2147483645 1。输出很简单:2147483645。但是现在我的最小值是1,因为2147483647/2147483647 + 1/2147483645 = 1,所以程序永远不会找到解决方案。我想制作一个优先级队列,然后删除最小值,然后将其与估计的最小值进行比较,如果这个最小值高于估计值,我可以使用该最小值而不是估计值,但它会..
  • .. 几乎总是有队列的最小值而不是估计的,这使得程序变慢,最大值也不会是 2*min。有什么建议可以更轻松地完成吗?
  • 上限被高估了。在之前的评论中,我的意思是你可以想出一个更小的上限。
猜你喜欢
  • 1970-01-01
  • 2013-10-30
  • 2013-02-02
  • 1970-01-01
  • 2012-01-01
  • 2014-12-18
  • 2021-03-11
  • 2013-02-03
  • 2017-06-08
相关资源
最近更新 更多