【问题标题】:Reactive Extensions: Getting the "aperture" of a binary number反应式扩展:获取二进制数的“孔径”
【发布时间】:2014-11-26 23:09:23
【问题描述】:

一位朋友提出了这个挑战。只是为了培训,我尝试使用 Reactive Extensions 解决它,但我没有运气。这并不奇怪,因为我还是 Rx 的新手。

这就是问题所在:

正整数 N 内的孔径是 其二进制表示中的连续零被包围 两端的。

例如,数字 9 具有二进制表示 1001 并包含一个 长度为 2 的孔径。数字 529 具有二进制表示 1000010001 并包含两个孔:一个长度为 4,另一个为 长度 3. 数字 20 具有二进制表示 10100 并包含 一个长度为 1 的孔径。数字 15 的二进制表示为 1111 并且没有光圈。编写一个函数:class Aperture { public int 孔径(int N); },给定一个正整数 N,返回 其最长孔径的长度。如果 N,该函数应返回 0 不包含光圈。假设:N是一个整数 范围 [1..2,147,483,647] 复杂度:算法时间复杂度为 O(log(N));

算法空间复杂度为 O(1)(最坏情况——不计算输入) 参数)

为简化起见,我尝试将其应用于“1000010001”之类的字符串,而不是 二进制表示。

无论如何,我不介意复杂性部分,只是我想知道一种“优雅”的方式来做到这一点。

【问题讨论】:

  • 听起来像是你的“朋友”的一项有趣的家庭作业。因为我正在学习它,所以我明天将在 Haskell 中尝试一下。 :-)

标签: c# algorithm system.reactive reactive-programming


【解决方案1】:

显然,在 RX 中解决这个问题没有什么意义,因为答案只能通过检查整个数字来推断 - 并且由于一大堆其他原因,它的效率非常低......

...但是为了您的娱乐:),这是一种非常愚蠢的 RX 方式(不要在家里这样做!)

public int Aperture(int input)
{
    var cs = Convert.ToString(input,2).ToCharArray().ToObservable();     

    return cs.Publish(ps => ps.Buffer(() => ps.Where(c => c == '1')))
    .Where(x => x.LastOrDefault() == '1')
    .Select(x => x.Count - 1).StartWith(0)
    .Max().Wait();
}

Aperture(9)   = 2
Aperture(529) = 4
Aperture(20)  = 1
Aperture(15)  = 0

这是另一种方式!

我不确定我为什么要这样做 :) 但这是另一种方式,它有点强。我基本上使用 2 元组作为累加器。我在一侧存储 0 的运行计数。如果我看到 1,我将计数复制到结果槽如果它高于那里的值,然后重置计数。结果槽包含最后的孔径。

public int Aperture(int input)
{    
    var cs = Convert.ToString(input,2).ToCharArray().ToObservable();

    return cs.Aggregate(
        Tuple.Create(0,0), (acc, c) => c == '0'
            ? Tuple.Create(acc.Item1 + 1, acc.Item2)
            : Tuple.Create(0, Math.Max(acc.Item1, acc.Item2)
        )).Wait().Item2;
}

另外,只需从上面删除ToCharArray().ToObservable()Wait(),您就有了IEnumerable<T> 版本!

【讨论】:

  • 令人印象深刻。还有一个问题:最后的“Wait()”是做什么的?
  • 有点像Task.Result,它等待可观察的流完成并返回最后一个元素。
  • @SuperJMN 忍不住,我又加了一个方法。
【解决方案2】:

我不知道如何在这里使用 Rx。我的解决方案说明了一种经典方法:

private static int Aperture(int n)
{
    int max = 0;
    int index = 0;
    int lastIndex = int.MaxValue;
    while (n != 0)
    {
        int bit;
        n = Math.DivRem(n, 2, out bit);
        if (bit != 0)
        {
            int length = index - lastIndex - 1;
            if (length > max)
                max = length;
            lastIndex = index;
        }
        index++;
    }
    return max;
}

测试结果:

Aperture(9)   = 2
Aperture(529) = 4
Aperture(20)  = 1
Aperture(15)  = 0

【讨论】:

    【解决方案3】:

    我同意为此使用 Rx 是愚蠢的。用 LINQ 解决问题同样傻,不过既然是 Rx 的老大哥,你也可以。

    与 James World 的回答一样,这仅用于娱乐目的。

    public int Aperture(int input)
    {
        var binaryString = Convert.ToString(input, 2);
    
        // The accumulator is an integer array maintaining
        // the count of '0's since the last seen '1'.
        // Whenever a '1' is encountered, a new count
        // of zero is added at the end of the array.
        // Whenever a '0' is encountered, the last
        // count is incremented by one.
        var segments = binaryString.Aggregate(
            new [] { 0 },
            (acc, c) =>
                c == '0'
                ? acc
                    .Take(acc.Length - 1)
                    .Concat(new [] { acc[acc.Length - 1] + 1 })
                    .ToArray()
                : acc
                    .Concat(new [] { 0 })
                    .ToArray()
        );
    
        return segments
            // If last segment count is non-zero, it was not
            // closed with a '1' and we want to exclude it.
            .Take(segments.Length - 1)
            .Max();
    }
    
    [TestMethod]
    public void ApertureTest()
    {
        Assert.AreEqual(2, Aperture(9));   // 1001,       Segments: 0, 2, 0
        Assert.AreEqual(4, Aperture(529)); // 1000010001, Segments: 0, 4, 3, 0
        Assert.AreEqual(1, Aperture(20));  // 10100,      Segments: 0, 1, 2
        Assert.AreEqual(0, Aperture(15));  // 1111,       Segments: 0, 0, 0, 0
    }
    

    现在我觉得很脏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      相关资源
      最近更新 更多