关于 Haskell 的问题 -> C# 转换

发布于 2024-11-09 01:41:16 字数 5479 浏览 3 评论 0原文

背景:

我被“拖”到看到这个问题: Haskell 中的斐波那契闭式表达式
当作者最初用许多其他语言标记但后来关注 Haskell 问题时。不幸的是,我对 Haskell 没有任何经验,所以我无法真正参与这个问题。然而,答案之一引起了我的注意,回答者转身它变成了一个纯整数数学问题。这对我来说听起来太棒了,所以我必须弄清楚它是如何工作的,并将其与递归斐波那契实现进行比较,看看它有多准确。我有一种感觉,如果我只记住涉及无理数的相关数学,我也许能够自己解决所有问题(但我没有)。所以我的第一步是将其移植到我熟悉的语言。在本例中,我正在执行 C#。

幸运的是我并没有完全蒙在鼓里。我在另一种函数式语言(OCaml)方面拥有丰富的经验,因此其中很多内容对我来说看起来有些熟悉。从转换开始,一切看起来都很简单,因为它基本上定义了一个新的数字类型来帮助计算。然而,我在翻译过程中遇到了一些障碍,无法完成它。我得到完全错误的结果。

分析:

这是我正在翻译的代码:

data Ext = Ext !Integer !Integer
    deriving (Eq, Show)

instance Num Ext where
    fromInteger a = Ext a 0
    negate (Ext a b) = Ext (-a) (-b)
    (Ext a b) + (Ext c d) = Ext (a+c) (b+d)
    (Ext a b) * (Ext c d) = Ext (a*c + 5*b*d) (a*d + b*c) -- easy to work out on paper
    -- remaining instance methods are not needed

fib n = divide $ twoPhi^n - (2-twoPhi)^n
  where twoPhi = Ext 1 1
        divide (Ext 0 b) = b `div` 2^n -- effectively divides by 2^n * sqrt 5

因此,根据我的研究和我可以推断出的内容(如果我有任何错误,请纠正我),第一部分声明类型 Ext ,其构造函数将有两个 Integer 参数(我猜会继承 Eq 和 Show 类型/模块)。

接下来是 Ext 的实现,它“派生”自 NumfromInteger 执行从 Integer 的转换。 negate 是一元否定,然后是二元加法和乘法运算符。

最后一部分是斐波那契的实际实现。

问题:

在答案中,hammar(回答者)提到求幂是由 Num 中的默认实现处理的。但这是什么意思?它实际上如何应用于这种类型?我是否缺少隐式数字“字段”?它只是将幂应用于它包含的每个相应数字吗?我假设它执行后者并最终得到此 C# 代码:

public static Ext operator ^(Ext x, int p) // "exponent"
{
    // just apply across both parts of Ext?
    return new Ext(BigInt.Pow(x.a, p), BigInt.Pow(x.b, p));
    //     Ext     (a^p)               (b^p)
}

但这与我理解为什么需要 negate 的方式相冲突,如果实际发生这种情况,它就不需要它。


Now the meat of the code. I read the first part divide $ twoPhi^n - (2-twoPhi)^n as:

将以下表达式的结果相除:twoPhi^n - (2-twoPhi)^n。

很简单。将 twoPhi 计算到 n 次方。从中减去其余的结果。这里我们进行二元减法,但只实现一元求反。或者我们没有?或者可以隐含二进制减法,因为它可以由加法和求反(我们有)组合而成?我认为是后者。这减轻了我对否定的不确定性。


The last part is the actual division: divide (Ext 0 b) = b `div` 2^n. Two concerns here. From what I've found, there is no division operator, only a `div` function. So I would just have to divide the numbers here. Is this correct? Or is there a division operator but a separate `div` function that does something else special?

我不确定如何解释该行的开头。这只是简单的模式匹配吗?换句话说,这是否仅适用于第一个参数为 0 的情况?如果不匹配(第一个非零)结果会是什么?或者我应该将其解释为我们不关心第一个参数并无条件应用该函数?这似乎是最大的障碍,使用任何一种解释仍然会产生不正确的结果。

我在任何地方做出了错误的假设吗?还是我只是错误地实现了 C#?

代码:

这是(非工作)翻译和完整源(包括测试)到目前为止,以防万一有人感兴趣。

// code removed to keep post size down
// full source still available through link above

进展:

好的,看看到目前为止的答案和评论,我想我知道从这里到哪里去以及为什么。

鉴于我们已经实现了乘法运算,求幂只需执行其通常的操作,即乘以 p 次。我从来没有想过我们应该做数学课上一直告诉我们要做的事情。加法和否定的隐含减法也是一个非常方便的功能。

在我的实现中还发现了一个拼写错误。当我应该乘数的时候我就加了。

// (Ext a b) * (Ext c d) = Ext (a*c + 5*b*d) (a*d + b*c)
public static Ext operator *(Ext x, Ext y)
{
    return new Ext(x.a * y.a + 5*x.b*y.b, x.a*y.b + x.b*y.a);
    //                 ^ oops!
}

结论:

现在已经完成了。我只实现了基本操作员并对其进行了一些重命名。命名方式与复数类似。到目前为止,即使输入非常大,也与递归实现保持一致。这是最终的代码。

static readonly Complicated TWO_PHI = new Complicated(1, 1);
static BigInt Fib_x(int n)
{
    var x = Complicated.Pow(TWO_PHI, n) - Complicated.Pow(2 - TWO_PHI, n);
    System.Diagnostics.Debug.Assert(x.Real == 0);
    return x.Bogus / BigInt.Pow(2, n);
}

struct Complicated
{
    private BigInt real;
    private BigInt bogus;

    public Complicated(BigInt real, BigInt bogus)
    {
        this.real = real;
        this.bogus = bogus;
    }
    public BigInt Real { get { return real; } }
    public BigInt Bogus { get { return bogus; } }

    public static Complicated Pow(Complicated value, int exponent)
    {
        if (exponent < 0)
            throw new ArgumentException(
                "only non-negative exponents supported",
                "exponent");

        Complicated result = 1;
        Complicated factor = value;
        for (int mask = exponent; mask != 0; mask >>= 1)
        {
            if ((mask & 0x1) != 0)
                result *= factor;
            factor *= factor;
        }
        return result;
    }

    public static implicit operator Complicated(int real)
    {
        return new Complicated(real, 0);
    }

    public static Complicated operator -(Complicated l, Complicated r)
    {
        var real = l.real - r.real;
        var bogus = l.bogus - r.bogus;
        return new Complicated(real, bogus);
    }

    public static Complicated operator *(Complicated l, Complicated r)
    {
        var real = l.real * r.real + 5 * l.bogus * r.bogus;
        var bogus = l.real * r.bogus + l.bogus * r.real;
        return new Complicated(real, bogus);
    }
}

这是完全更新的源

Background:

I was "dragged" into seeing this question:
Fibonacci's Closed-form expression in Haskell
when the author initially tagged with many other languages but later focused to a Haskell question. Unfortunately I have no experience whatsoever with Haskell so I couldn't really participate in the question. However one of the answers caught my eye where the answerer turned it into a pure integer math problem. That sounded awesome to me so I had to figure out how it worked and compare this to a recursive Fibonacci implementation to see how accurate it was. I have a feeling that if I just remembered the relevant math involving irrational numbers, I might be able to work everything out myself (but I don't). So the first step for me was to port it to a language I am familiar with. In this case, I am doing C#.

I am not completely in the dark fortunately. I have plenty experience in another functional language (OCaml) so a lot of it looked somewhat familiar to me. Starting out with the conversion, everything seemed straightforward since it basically defined a new numeric type to help with the calculations. However I've hit a couple of roadblocks in the translation and am having trouble finishing it. I'm getting completely wrong results.

Analysis:

Here's the code that I'm translating:

data Ext = Ext !Integer !Integer
    deriving (Eq, Show)

instance Num Ext where
    fromInteger a = Ext a 0
    negate (Ext a b) = Ext (-a) (-b)
    (Ext a b) + (Ext c d) = Ext (a+c) (b+d)
    (Ext a b) * (Ext c d) = Ext (a*c + 5*b*d) (a*d + b*c) -- easy to work out on paper
    -- remaining instance methods are not needed

fib n = divide $ twoPhi^n - (2-twoPhi)^n
  where twoPhi = Ext 1 1
        divide (Ext 0 b) = b `div` 2^n -- effectively divides by 2^n * sqrt 5

So based on my research and what I can deduce (correct me if I'm wrong anywhere), the first part declares type Ext with a constructor that will have two Integer parameters (and I guess will inherit the Eq and Show types/modules).

Next is the implementation of Ext which "derives" from Num. fromInteger performs a conversion from an Integer. negate is the unary negation and then there's the binary addition and multiplication operators.

The last part is the actual Fibonacci implementation.

Questions:

In the answer, hammar (the answerer) mentions that exponentiation is handled by the default implementation in Num. But what does that mean and how is that actually applied to this type? Is there an implicit number "field" that I'm missing? Does it just apply the exponentiation to each corresponding number it contains? I assume it does the latter and end up with this C# code:

public static Ext operator ^(Ext x, int p) // "exponent"
{
    // just apply across both parts of Ext?
    return new Ext(BigInt.Pow(x.a, p), BigInt.Pow(x.b, p));
    //     Ext     (a^p)               (b^p)
}

However this conflicts with how I perceive why negate is needed, it wouldn't need it if this actually happens.


Now the meat of the code. I read the first part divide $ twoPhi^n - (2-twoPhi)^n as:

divide the result of the following expression: twoPhi^n - (2-twoPhi)^n.

Pretty simple. Raise twoPhi to the nth power. Subtract from that the result of the rest. Here we're doing binary subtraction but we only implemented unary negation. Or did we not? Or can binary subtraction be implied because it could be made up combining addition and negation (which we have)? I assume the latter. And this eases my uncertainty about the negation.


The last part is the actual division: divide (Ext 0 b) = b `div` 2^n. Two concerns here. From what I've found, there is no division operator, only a `div` function. So I would just have to divide the numbers here. Is this correct? Or is there a division operator but a separate `div` function that does something else special?

I'm not sure how to interpret the beginning of the line. Is it just a simple pattern match? In other words, would this only apply if the first parameter was a 0? What would the result be if it didn't match (the first was non-zero)? Or should I be interpreting it as we don't care about the first parameter and apply the function unconditionally? This seems to be the biggest hurdle and using either interpretation still yields the incorrect results.

Did I make any wrong assumptions anywhere? Or is it all right and I just implemented the C# incorrectly?

Code:

Here's the (non-working) translation and the full source (including tests) so far just in case anyone is interested.

// code removed to keep post size down
// full source still available through link above

Progress:

Ok so looking at the answers and comments so far, I think I know where to go from here and why.

The exponentiation just needed to do what it normally does, multiply p times given that we've implemented the multiply operation. It never crossed my mind that we should do what math class has always told us to do. The implied subtraction from having addition and negation is a pretty handy feature too.

Also spotted a typo in my implementation. I added when I should have multiplied.

// (Ext a b) * (Ext c d) = Ext (a*c + 5*b*d) (a*d + b*c)
public static Ext operator *(Ext x, Ext y)
{
    return new Ext(x.a * y.a + 5*x.b*y.b, x.a*y.b + x.b*y.a);
    //                 ^ oops!
}

Conclusion:

So now it's completed. I only implemented to essential operators and renamed it a bit. Named in a similar manner as complex numbers. So far, consistent with the recursive implementation, even at really large inputs. Here's the final code.

static readonly Complicated TWO_PHI = new Complicated(1, 1);
static BigInt Fib_x(int n)
{
    var x = Complicated.Pow(TWO_PHI, n) - Complicated.Pow(2 - TWO_PHI, n);
    System.Diagnostics.Debug.Assert(x.Real == 0);
    return x.Bogus / BigInt.Pow(2, n);
}

struct Complicated
{
    private BigInt real;
    private BigInt bogus;

    public Complicated(BigInt real, BigInt bogus)
    {
        this.real = real;
        this.bogus = bogus;
    }
    public BigInt Real { get { return real; } }
    public BigInt Bogus { get { return bogus; } }

    public static Complicated Pow(Complicated value, int exponent)
    {
        if (exponent < 0)
            throw new ArgumentException(
                "only non-negative exponents supported",
                "exponent");

        Complicated result = 1;
        Complicated factor = value;
        for (int mask = exponent; mask != 0; mask >>= 1)
        {
            if ((mask & 0x1) != 0)
                result *= factor;
            factor *= factor;
        }
        return result;
    }

    public static implicit operator Complicated(int real)
    {
        return new Complicated(real, 0);
    }

    public static Complicated operator -(Complicated l, Complicated r)
    {
        var real = l.real - r.real;
        var bogus = l.bogus - r.bogus;
        return new Complicated(real, bogus);
    }

    public static Complicated operator *(Complicated l, Complicated r)
    {
        var real = l.real * r.real + 5 * l.bogus * r.bogus;
        var bogus = l.real * r.bogus + l.bogus * r.real;
        return new Complicated(real, bogus);
    }
}

And here's the fully updated source.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

双手揣兜 2024-11-16 01:41:16

[...],第一部分使用构造函数声明 Ext 类型,该构造函数将具有两个 Integer 参数(我猜将继承 Eq 和 Show 类型/模块)。

EqShow类型类。您可以将它们视为类似于 C# 中的接口,只是更强大。 deriving 是一种构造,可用于自动生成少数标准类型类的实现,包括 EqShowOrd 等。这减少了您必须编写的样板文件的数量。

instance Num Ext 部分提供了 Num 类型类的显式实现。这部分的大部分内容你都答对了。

[回答者]提到求幂是由 Num 中的默认实现处理的。但这是什么意思?它实际上如何应用于这种类型?我是否缺少隐式数字“字段”?它只是将幂应用于它包含的每个相应数字吗?

我对此有点不清楚。 ^ 不在类型类 Num 中,但它是完全根据 Num 方法定义的辅助函数,有点像扩展方法。它通过二进制求幂实现正积分幂的求幂。这是代码的主要“技巧”。

[...] 我们正在进行二进制减法,但我们只实现了一元求反。或者我们没有?或者可以隐含二进制减法,因为它可以由加法和求反(我们有)组合而成?

正确的。二进制减法的默认实现是 x - y = x + (negate y)。

最后一部分是实际除法:divide (Ext 0 b) = b `div` 2^n。这里有两个问题。据我发现,没有除法运算符,只有 div 函数。所以我只需要把这里的数字相除即可。这是正确的吗?或者是否有一个除法运算符,但有一个单独的 div 函数可以执行其他特殊操作?

Haskell 中的运算符和函数之间仅存在语法差异。我们可以通过将运算符写在括号(+) 中将其视为函数,或者将函数写在`反引号` 中将其视为二元运算符。

div 是整数除法,属于类型类 Integral,因此它是为所有类似整数的类型定义的,包括 Int(机器大小)整数)和Integer(任意大小的整数)。

我不知道如何解释该行的开头。这只是简单的模式匹配吗?换句话说,这是否仅适用于第一个参数为 0 的情况?如果不匹配(第一个非零)结果会是什么?或者我应该将其解释为我们不关心第一个参数并无条件应用该函数?

确实只是一个简单的模式匹配来提取√5的系数。整数部分与零匹配,以向读者表达我们确实希望它始终为零,并且如果代码中的某些错误导致它不为零,则使程序崩溃。


一个小改进

将原代码中的Integer替换为Rational,您可以将fib n写得更接近比奈公式

fib n = divSq5 $ phi^n - (1-phi)^n
  where divSq5 (Ext 0 b) = numerator b
        phi = Ext (1/2) (1/2)

这在整个计算过程中执行除法,而不是将其全部保存起来以供计算结尾。这会导致计算 fib (10^6) 时中间数更小,加速大约 20%。

[...], the first part declares type Ext with a constructor that will have two Integer parameters (and I guess will inherit the Eq and Show types/modules).

Eq and Show are type classes. You can think of them as similar to interfaces in C#, only more powerful. deriving is a construct that can be used to automatically generate implementations for a handful of standard type classes, including Eq, Show, Ord and others. This reduces the amount of boilerplate you have to write.

The instance Num Ext part provides an explicit implementation of the Num type class. You got most of this part right.

[the answerer] mentions that exponentiation is handled by the default implementation in Num. But what does that mean and how is that actually applied to this type? Is there an implicit number "field" that I'm missing? Does it just apply the exponentiation to each corresponding number it contains?

This was a bit unclear on my part. ^ is not in the type class Num, but it is an auxilliary function defined entirely in terms of the Num methods, sort of like an extension method. It implements exponentiation to positive integral powers through binary exponentiation. This is the main "trick" of the code.

[...] we're doing binary subtraction but we only implemented unary negation. Or did we not? Or can binary subtraction be implied because it could be made up combinding addition and negation (which we have)?

Correct. The default implementation of binary minus is x - y = x + (negate y).

The last part is the actual division: divide (Ext 0 b) = b `div` 2^n. Two concerns here. From what I've found, there is no division operator, only a div function. So I would just have to divide the numbers here. Is this correct? Or is there a division operator but a separate div function that does something else special?

There is only a syntactic difference between operators and functions in Haskell. One can treat an operator as a function by writing it parenthesis (+), or treat a function as a binary operator by writing it in `backticks`.

div is integer division and belongs to the type class Integral, so it is defined for all integer-like types, including Int (machine-sized integers) and Integer (arbitrary-size integers).

I'm not sure how to interpret the beginning of the line. Is it just a simple pattern match? In other words, would this only apply if the first parameter was a 0? What would the result be if it didn't match (the first was non-zero)? Or should I be interpreting it as we don't care about the first parameter and apply the function unconditionally?

It is indeed just a simple pattern match to extract the coefficient of √5. The integral part is matched against a zero to express to readers that we indeed expect it to always be zero, and to make the program crash if some bug in the code was causing it not to be.


A small improvement

Replacing Integer with Rational in the original code, you can write fib n even closer to Binet's formula:

fib n = divSq5 $ phi^n - (1-phi)^n
  where divSq5 (Ext 0 b) = numerator b
        phi = Ext (1/2) (1/2)

This performs the divisions throughout the computation, instead of saving it all up for the end. This results in smaller intermediate numbers and about 20% speedup when calculating fib (10^6).

久隐师 2024-11-16 01:41:16

首先,NumShowEq是类型类,而不是类型或模块。它们有点类似于 C# 中的接口,但是静态解析的,而不是动态解析的。

其次,求幂是通过乘法与 ^ 的实现来执行的,它不是 Num 类型类的成员,而是一个单独的函数。

实现如下:

(^) :: (Num a, Integral b) => a -> b -> a
x0 ^ y0 | y0 < 0    = error "Negative exponent"
        | y0 == 0   = 1
        | otherwise = f x0 y0
    where -- f : x0 ^ y0 = x ^ y
          f x y | even y    = f (x * x) (y `quot` 2)
                | y == 1    = x
                | otherwise = g (x * x) ((y - 1) `quot` 2) x
          -- g : x0 ^ y0 = (x ^ y) * z
          g x y z | even y = g (x * x) (y `quot` 2) z
                  | y == 1 = x * z
                  | otherwise = g (x * x) ((y - 1) `quot` 2) (x * z)

这似乎是解决方案中缺少的部分。

关于减法,你是对的。它是通过加法和否定来实现的。

现在,divide 函数仅当 a 等于 0 时才进行除法。否则我们会得到模式匹配失败,表明程序中存在错误。

div 函数是一个简单的整数除法,相当于 C# 中应用于整数类型的 /。 Haskell中还有一个运算符/,但它表示实数除法。

First, Num, Show, Eq are type classes, not types nor modules. They are a bit similar to interfaces in C#, but are resolved statically rather than dynamically.

Second, exponentiation is performed via multiplication with the implementation of ^, which is not a member of the Num typeclass, but a separate function.

The implementation is the following:

(^) :: (Num a, Integral b) => a -> b -> a
x0 ^ y0 | y0 < 0    = error "Negative exponent"
        | y0 == 0   = 1
        | otherwise = f x0 y0
    where -- f : x0 ^ y0 = x ^ y
          f x y | even y    = f (x * x) (y `quot` 2)
                | y == 1    = x
                | otherwise = g (x * x) ((y - 1) `quot` 2) x
          -- g : x0 ^ y0 = (x ^ y) * z
          g x y z | even y = g (x * x) (y `quot` 2) z
                  | y == 1 = x * z
                  | otherwise = g (x * x) ((y - 1) `quot` 2) (x * z)

This seems to be the missing part of solution.

You are right about subtraction. It is implemented via addition and negation.

Now, the divide function divides only if a equals to 0. Otherwise we get a pattern match failure, indicating a bug in the program.

The div function is a simple integer division, equivalent to / applied to integral types in C#. There is also an operator / in Haskell, but it indicates real number division.

流年里的时光 2024-11-16 01:41:16

C# 中的快速实现。我使用平方乘算法实现了求幂。

a+b*Sqrt(5) 形式的这种类型与 a+b*Sqrt(-1) 形式的复数进行比较是有启发性的。加法和减法的工作原理是一样的。乘法略有不同,因为这里 i^2 不是 -1 而是 +5。除法稍微复杂一些,但也不会太难。

求幂被定义为将一个数与其自身相乘 n 次。但这当然很慢。因此,我们利用 ((a*a)*a)*a(a*a)*(a*a) 相同的事实,并使用平方重写乘法算法。因此,我们只需要 log(n) 次乘法,而不是 n 次乘法。

仅计算各个分量的指数是行不通的。那是因为你的类型背后的矩阵不是对角的。将此与复数的性质进行比较。您不能简单地分别计算实部和虚部的指数。

struct MyNumber
{
    public readonly BigInteger Real;
    public readonly BigInteger Sqrt5;

    public MyNumber(BigInteger real,BigInteger sqrt5)
    {
        Real=real;
        Sqrt5=sqrt5;
    }

    public static MyNumber operator -(MyNumber left,MyNumber right)
    {
        return new MyNumber(left.Real-right.Real, left.Sqrt5-right.Sqrt5);
    }

    public static MyNumber operator*(MyNumber left,MyNumber right)
    {
        BigInteger real=left.Real*right.Real + left.Sqrt5*right.Sqrt5*5;
        BigInteger sqrt5=left.Real*right.Sqrt5 + right.Real*left.Sqrt5;
        return new MyNumber(real,sqrt5);
    }

    public static MyNumber Power(MyNumber b,int exponent)
    {
        if(!(exponent>=0))
            throw new ArgumentException();
        MyNumber result=new MyNumber(1,0);
        MyNumber multiplier=b;
        while(exponent!=0)
        {
            if((exponent&1)==1)//exponent is odd
                result*=multiplier;
            multiplier=multiplier*multiplier;
            exponent/=2;
        }
        return result;
    }

    public override string ToString()
    {
        return Real.ToString()+"+"+Sqrt5.ToString()+"*Sqrt(5)";
    }
}

BigInteger Fibo(int n)
{
    MyNumber num = MyNumber.Power(new MyNumber(1,1),n)-MyNumber.Power(new MyNumber(1,-1),n);
    num.Dump();
    if(num.Real!=0)
      throw new Exception("Asser failed");
    return num.Sqrt5/BigInteger.Pow(2,n);
}

void Main()
{
  MyNumber num=new MyNumber(1,2);
  MyNumber.Power(num,2).Dump();
  Fibo(5).Dump();
}

A quick implementation in C#. I implemented exponentiation using the square-and-multiply algorithm.

It is enlightening to compare this type which has the form a+b*Sqrt(5) with the complex numbers which take the form a+b*Sqrt(-1). Addition and subtraction work just the same. Multiplication is slightly different, because i^2 isn't -1 but +5 here. Division is slightly more complicated, but shouldn't be too hard either.

Exponentiation is defined as multiplying a number with itself n times. But of course that's slow. So we use the fact that ((a*a)*a)*a is identical to (a*a)*(a*a) and rewrite using the square-and-multiply algorithm. So we just need log(n) multiplications instead of n multiplications.

Just calculating the exponential of the individual components doesn't work. That's because the matrix underlying your type isn't diagonal. Compare this to the property of complex numbers. You can't simply calculate the exponential of the real and imaginary part separately.

struct MyNumber
{
    public readonly BigInteger Real;
    public readonly BigInteger Sqrt5;

    public MyNumber(BigInteger real,BigInteger sqrt5)
    {
        Real=real;
        Sqrt5=sqrt5;
    }

    public static MyNumber operator -(MyNumber left,MyNumber right)
    {
        return new MyNumber(left.Real-right.Real, left.Sqrt5-right.Sqrt5);
    }

    public static MyNumber operator*(MyNumber left,MyNumber right)
    {
        BigInteger real=left.Real*right.Real + left.Sqrt5*right.Sqrt5*5;
        BigInteger sqrt5=left.Real*right.Sqrt5 + right.Real*left.Sqrt5;
        return new MyNumber(real,sqrt5);
    }

    public static MyNumber Power(MyNumber b,int exponent)
    {
        if(!(exponent>=0))
            throw new ArgumentException();
        MyNumber result=new MyNumber(1,0);
        MyNumber multiplier=b;
        while(exponent!=0)
        {
            if((exponent&1)==1)//exponent is odd
                result*=multiplier;
            multiplier=multiplier*multiplier;
            exponent/=2;
        }
        return result;
    }

    public override string ToString()
    {
        return Real.ToString()+"+"+Sqrt5.ToString()+"*Sqrt(5)";
    }
}

BigInteger Fibo(int n)
{
    MyNumber num = MyNumber.Power(new MyNumber(1,1),n)-MyNumber.Power(new MyNumber(1,-1),n);
    num.Dump();
    if(num.Real!=0)
      throw new Exception("Asser failed");
    return num.Sqrt5/BigInteger.Pow(2,n);
}

void Main()
{
  MyNumber num=new MyNumber(1,2);
  MyNumber.Power(num,2).Dump();
  Fibo(5).Dump();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文