递归算法(一)——akm

要求

已知akm函数如下:

{ n+1 while m=0 }                          => Rule I

akm(m,n)= { akm(m-1,1) while n=0 }                => Rule II

{ akm(m-1,akm(m,n-1)) otherwise } => Rule III

写出递归与非递归算法,并输出调用过程。

实现

参见https://github.com/bajdcc/ALGImplements/blob/master/akm/akm.cpp

#include "stdafx.h"
#include <stdio.h>
#include <stack>

/*
 * Ackerman function definition
 *
 *           {  n+1                    while m=0  } => Rule I
 * akm(m,n)= {  akm(m-1,1)             while n=0  } => Rule II
 *           {  akm(m-1,akm(m,n-1))    otherwise  } => Rule III
 */

 //************************************
 // Method:    akm1
 // FullName:  递归解法
 // Access:    public
 // Returns:   int
 // Qualifier: recursion
 // Parameter: int m
 // Parameter: int n
 //************************************
int akm1(int m, int n)
{
    printf("Compute akm(%d,%d)\n", m, n);
    if (m == 0)
    {
        printf("Compute akm(%d,%d) - val = %d\n", m, n, n + 1);
        return n + 1;
    }
    else if (n == 0)
    {
        return akm1(m - 1, 1);
    }
    else
    {
        return akm1(m - 1, akm1(m, n - 1));
    }
}

//************************************
// Method:    akm2
// FullName:  非递归解法一
// Access:    public
// Returns:   int
// Qualifier: non-recursion
// Parameter: int m
// Parameter: int n
//************************************
int akm2(int m, int n)
{
    /* 这个解法有其特殊性,因为递归调用数为一,没有并列调用(一个以上参数为递归调用,如akm(akm(...),akm(...))) */
    //这样,在栈中,调用关系为线性关系,而不是树形关系

    //栈的惰性求值方法,若表达式中含有非求值akm函数,则记录它在链表中的位置
    //由于存在m和n参数以及akm函数返回值,因此一个结点中有m,n,val
    struct akm_node
    {
        int m, n, val;
    };
    std::stack<akm_node> exp_stack;
    akm_node initial_val;//初始值
    initial_val.m = m;
    initial_val.n = n;
    initial_val.val = -1;//返回值尚未求得
    exp_stack.push(initial_val);
    /* 注意:这个方法假设akm的值为正数 */
    while (true)//主循环
    {
        akm_node& node = exp_stack.top();//当前处理的表达式,取栈顶且不出栈,取引用说明要修改
        printf("Compute akm(%d,%d)\n", node.m, node.n);
        //为什么为top不急着出栈,这是因为之前的表达式要用到当前值
        //此时出栈后,之前的式子就没有获得返回值
        //出栈的最佳时机,即为式子取得其参数(返回值)后
        if (node.val != -1)//值被求出
        {
            //什么时候才是出栈的时机,这个时候!
            //此时,值已经求得,即为可解问题
            //我们知道可解问题一般为树的叶子结点
            //求得这个值之后,可能这个结果是上一次调用的参数
            //那么应该亡羊补牢,赶紧补上上一次调用的参数
            exp_stack.pop();//值在node中,故可出栈
            if (exp_stack.empty())//Rule I
            {
                //假使当前栈空,那么这个node就是最终值(所有嵌套全部计算完毕),九九归一
                return node.val;
            }
            else
            {
                //栈不空,说明【肯定】含有待解问题
                //因为当且仅当栈空时,调用栈为空,即树归约到根结点,得到最终结果
                akm_node& not_computed_exp = exp_stack.top();//取引用说明要修改
                if (not_computed_exp.n == 1)//Rule II called Rule I
                {
                    not_computed_exp.val = node.val;//give it computed value
                }
                else if (not_computed_exp.n == -1)//Rule III called Rule I
                {
                    not_computed_exp.n = node.val;
                }
                else
                {
                    not_computed_exp.val = node.val;
                }
            }
            continue;
        }
        if (node.m == 0)//Rule I: m+1 while m equals to zero
        {
            printf("Compute akm(%d,%d) - val = %d\n", node.m, node.n, node.n + 1);
            node.val = node.n + 1;
        }
        else if (node.n == 0)//Rule II akm(m-1,1) while n equals to zero
        {
            //此时值不可直接求解,故需要嵌套求值(惰性求值)
            //将二次调用参数进栈
            akm_node secondary_call_exp;
            secondary_call_exp.m = node.m - 1;
            secondary_call_exp.n = 1;
            secondary_call_exp.val = -1;
            exp_stack.push(secondary_call_exp);
        }
        else//Rule III akm(m-1,akm(m,n-1)) otherwise
        {
            //此时值不可直接求解,故需要嵌套求值(惰性求值)
            akm_node secondary_call_exp;//二次调用
            secondary_call_exp.m = node.m - 1;
            secondary_call_exp.n = -1;//-1代表未知
            secondary_call_exp.val = -1;
            exp_stack.push(secondary_call_exp);
            akm_node tertiary_call_exp;//三次调用
            tertiary_call_exp.m = node.m;
            tertiary_call_exp.n = node.n - 1;
            tertiary_call_exp.val = -1;
            exp_stack.push(tertiary_call_exp);
            continue;
        }
    }
    return 0;
}

int main()
{
    printf("============ 递归 ===========\n");
    printf("akm(2,1)=%d\n", akm1(2, 1));
    printf("\n");
    printf("============ 非递归 ===========\n");
    printf("akm(2,1)=%d\n", akm2(2, 1));
    printf("\n");
    return 0;
}
时间: 2025-01-16 07:57:10

递归算法(一)——akm的相关文章

金山云笔试题:AKM函数

1. 题目描述 /** 阿克曼(Ackmann)函数 [题目描述] 阿克曼(Ackmann)函数A(m,n)中,m,n定义域是非负整数(m<=3,n<=10),函数值定义为: akm(m,n) = n+1;         (m=0时) akm(m,n) = akm(m-1,1);  (m>0,n=0时) akm(m,n) = akm(m-1,akm(m, n-1)); (m,n>0时) [输入]输入m和n. [输出]函数值. [输入样例]2 3 [输出样例]9 */ 写出递归与非

汉诺塔递归算法

利用递归算法: 第一步:将n-1个盘子移到B 第二步:将第n个盘子移到C 第三步:将n-1个盘子移到C #include<iostream> using namespace std; int count=0; void move(int n,char a,char b) //n表示盘子号a表示盘子的起始位置 b表示盘子到达的位置 { cout<<a<<"->"<<b<<endl; count++; } void towe

对递归算法的理解

1.递归的定义:程序调用自身的编程技巧称为递归.递归做为一种算法在程序设计语言中广泛应用.一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量.递归的能力在于用有限的语句来定义对象的无限集合.一般来说,递归需要有边界条件.递归前进段和递归返回段.当边界条件不满足时,递归前进:当边界条件满足时,递归返回. 2.递归的优点:代码简

简单递归算法题

#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2017/4/28 16:41 # @Author : MnCu # @Site : # @File : fbnq.py # @Software: PyCharm # 一对兔子从出生到可繁殖需两个月,然后每月都能繁殖一对兔子,问n月后共有多少兔子 def fbnc_func(n): ''' 1 出口: 当月份小于等于2月时,则返回1 2 f(n)与f(n-1)的关系: 当月的兔子数 =

全排列问题的递归算法(Perm)

[题目]设计一个递归算法生成n个元素{r1,r2,-,rn}的全排列. [算法讲解] 设R={r1,r2,-,rn}是要进行排列的n个元素,Ri=R-{ri}.集合X中元素的全排列记为perm(X).(ri)perm(X)表示在全排列perm(X)的每一个排列前加上前缀得到的排列.R的全排列可归纳定义如下: 当n=1时,perm(R)=(r),其中r是集合R中唯一的元素:当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),-,(rn)perm(Rn)构成.实现思想:将

java基础学习总结五(递归算法、冒泡排序)

一:递归算法 概念:自己调用自己的方法 示例代码如下: 1 @Test 2 /** 3 * 递归求和 4 * 5+4+3+2+1=15 5 */ 6 public void getSum() { 7 long sum = sum(5); 8 System.out.println("sum=" + sum); 9 } 10 11 public static long sum(int i) { 12 if (1 == i) { 13 return 1; 14 } else { 15 ret

递归算法解析成树形结构

/** * 递归算法解析成树形结构 * * @param cid * @return * @author jiqinlin */ public TreeNodeModel recursiveTree(int org_code) { //根据cid获取节点对象(SELECT * FROM tb_tree t WHERE t.cid=?) MiddleOrgEntity middleOrgEntity = new MiddleOrgEntity(); middleOrgEntity.setTable

递归算法(转)

递归算法设计的基本思想是:对于一个复杂的问题,把原问题分解为若干个相对简单类同的子问题,继续下去直到子问题简单到能够直接求解,也就是说到了递推的出口,这样原问题就有递推得解. 关键要抓住的是: (1)递归出口 (2)地推逐步向出口逼近 例子: example: 求5的阶乘..          如下: Java代码  public class Test { static int multiply(int n){ if(n==1||n==0) return n; else return n*mul

递归算法 c#

1.  算法思路 递归算法,就是一种直接或者间接调用自身算法.递归算法的具体实现过程一般通过函数或者子过程来完成,在函数或者子过程的内部,编写代码直接或者间接地调用自己即可完成递归操作. //阶乘 private static int Fact(int i) { if (i<=1) { return i; } else { return i * Fact(i - 1); } }