佩尔方程求解问题

关于佩尔方程,下面推荐几个不错的博客:

http://www.matrix67.com/blog/archives/5556

http://blog.sina.com.cn/s/blog_5d06e2390100ll92.html

http://m.blog.csdn.net/blog/wh2124335/8871535



HDU3292  No more tricks, Mr Nanguo    

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3292


题目大意:讲述的是滥竽充数的故事。有一个乐队,可以站成一个边长为x的正方形,也可以减去一个人,然后站成若干个边长为y的正方形,很明显乐队人数不是唯一的,现在让你找出第k大的乐队人数。


分析:根据题意可以提炼出二元不定方程x^2-D*y^2=1,典型的佩尔方程。首先我们知道,若D是完全平方数,那么方程无解;否则,可以暴力(也可以用连分数)求出方程的特解,然后矩阵快速幂求出方程第k大的解。


这题我用暴力求的特解,实现代码如下:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define mod 8191
#define N 2
typedef struct
{
    int m[N][N];
}matrix;
matrix per,d;
int n;
int x,y,D;
void search()
{
    y=1;
    while(true)
    {
        x=(int)sqrt(D*y*y+1.0);
        if(x*x-D*y*y==1) break;
        y++;
    }
}
void init()
{
    d.m[0][0]=x%mod;
    d.m[0][1]=D*y%mod;
    d.m[1][0]=y%mod;
    d.m[1][1]=x%mod;
    for(int i=0;i<N;i++)
      for(int j=0;j<N;j++)
        per.m[i][j]=(i==j);
}
matrix multi(matrix a,matrix b)
{
    matrix c;
    for(int i=0;i<N;i++)
      for(int j=0;j<N;j++)
      {
          c.m[i][j]=0;
          for(int k=0;k<N;k++)
            c.m[i][j]+=a.m[i][k]*b.m[k][j];
        c.m[i][j]%=mod;
      }
      return c;
}
matrix quick_mod(int k)
{
    matrix p=d,ans=per;
    while(k)
    {
        if(k&1)
        {
            ans=multi(ans,p);
            k--;
        }
        k>>=1;
        p=multi(p,p);
    }
    return ans;
}
int main()
{
    int K;
    while(cin>>D>>K)
    {
        int ad=(int)sqrt(D+0.0);
        if(ad*ad==D)
        {
            puts("No answers can meet such conditions");
            continue;
        }
        search();
        init();
        d=quick_mod(K-1);
        printf("%d\n",(d.m[0][0]*x%mod+d.m[0][1]*y%mod)%mod);
    }
    return 0;
}




POJ1320  Street Numbers

题目连接:http://poj.org/problem?id=1320


题目大意:一个程序员住在一条街道上,街道上的房屋按1到m编号,程序员出家门(假设程序员家的编号为n)左转,把每个房屋的编号(不包括自己家)加起来的值,等于出家门右转,把每个房屋(同样不包括自己家)的编号加起来的值,让你找出前10组符合这样n和m的值。


分析:题目描述可以转换为,求解两个不相等的正整数n和m(m>n),使得1+2+...+(n-1)=(n+1)+...+m,两边都为等差数列,化简一下可以得到:(n)×(n-1)=(m-n)×(m+n+1),进一步化简得:(2m+1)^2-8*n^2=1,即变为了佩尔方程的求解问题。对于方程x^2-8*y^2=1,很容易看出特解为x=3,y=1,那么由递推关系式xn=xn-1*x1+d*yn-1*y1;yn=xn-1*y1+yn-1*x1,得到xn+1=3*xn+8*yn,yn+1=xn+3*yn;


实现代码如下:

#include <cstdio>
using namespace std;
int main()
{
    int x,y;
    int x0=3,y0=1;
    for(int i=1;i<=10;i++)
    {
        x=3*x0+8*y0;
        y=x0+3*y0;
        printf("%10d%10d\n",y,(x-1)/2);
        x0=x,y0=y;
    }
    return 0;
}




poj2427  Smith's Problem

题目连接:http://poj.org/problem?id=2427


题目大意:求方程x^2-n*y^2=1的最小正整数解。


分析:连分数求佩尔方程特解。由于要用高精度,用java。


实现代码如下:

import java.math.BigInteger;  
import java.util.Scanner;  

public class Main
{  
    public static void solve(int n) 
    {  
        BigInteger N, p1, p2, q1, q2, a0, a1, a2, g1, g2, h1, h2,p,q;  
        g1 = q2 = p1 = BigInteger.ZERO;  
        h1 = q1 = p2 = BigInteger.ONE;  
        a0 = a1 = BigInteger.valueOf((int)Math.sqrt(1.0*n));
        BigInteger ans=a0.multiply(a0);
        if(ans.equals(BigInteger.valueOf(n)))
        {
        	System.out.println("No solution!");
        	return;
        }
        N = BigInteger.valueOf(n);  
        while (true) 
        {  
            g2 = a1.multiply(h1).subtract(g1);     
            h2 = N.subtract(g2.pow(2)).divide(h1);  
            a2 = g2.add(a0).divide(h2);          
            p = a1.multiply(p2).add(p1);           
            q = a1.multiply(q2).add(q1);          
            if (p.pow(2).subtract(N.multiply(q.pow(2))).compareTo(BigInteger.ONE) == 0) break;
            g1 = g2;h1 = h2;a1 = a2;  
            p1 = p2;p2 = p;  
            q1 = q2;q2 = q;  
        }
        System.out.println(p+" "+q);
    }  
   
    public static void main(String[] args) 
    {  
        Scanner cin = new Scanner(System.in);  
        while(cin.hasNextInt())
        {
            solve(cin.nextInt());   
        }
    }  
}  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值