两道编程题

昨天meta网友在某论坛写了两条编程题目:

  1. 设计一个函数f, 使得它满足:f(f(x))=-x,这里输入参数为32位整型
  2. 设计一个函数g, 满足:g(g(x))=1/x, x是浮点数

以下是一些反面的解答,可澄清这个问题:

  • meta提供了同事的解答,但该解答用了static local variable来區分办调用次数。这函数有副作用,且不是thread-safe。因此这不是好答案。
  • SweatingKng Zhu网友利用语言特性,第一次调用函数时,输出第二种型别,使第二次执行该函数时,用型别来检测这是第二次调用。这个方法其实等同写两个签名不一样的函数,和题目有出入,并不是正确答案。
  • Wang Feng网友利用复数去解题。不过如果输入输出能增加一个变量,不如直接用该变量来储存调用次数,就不需用复数了。

1.1  我的囧事

第一个回覆这帖子的是网友Atry,他解答了问题(1),但没有写当中的逻辑(其实和本文的解法思路一样)

另外,有网友认为两条问题是无解的,我也是当中一员。因此,今天午饭时间就发了以下的错误证明:

  1. 假设一个函数 f 存在,x 32-bit整数,f(x)) = -x
  2. y = f(x)
  3. f(f(x)) = -x f(y) = –x
  4. 变换变量, f(y) = -x f(x) = -y y = -f(x)
  5. y = f(x) = -f(x)

5步只是当y=0才成立,和f的值域矛盾,按反证法,函数f不存在。

Lu JunZhuHongzhang Liu网友指出第4行有错误。Hongzhang Liu更套用同样思路,可以错误地证明,f(f(x))=4x中的f是不存在的。那就肯定是我的错了,囧rz。我当时没想到错在那,就去请教郑晖老师。

郑老师指出,只有自由变量(Free Variable)才可以置换(Subsititue)。上述证明中,xy不是自由变量。之后,郑老师独立做了一个解答,我编程序来测试(虽然郑老师认为应该用证明方式)。以下我尝试把郑老师的解答,加上我的理解去演译一个正确答案。

1.2  问题分析

这两问题的难点在于,函数不能储存额外状态。

我们首先分析问题(1),设y=f(x),则

1. f(x) = y

2. f(y) = -x

如果再把结果-x再应用一次f 函数,f(-x) = ?

因为之前 f(y)=-x,而按题目定义,f(f(y))=-y ,所以f(-x) = -y。我们可以列出:

3. f(-x) = -y

4. f(-y) = x

我们可以发现,4次函数映射之后,会变成一个循环。也就是说,

x y –x –y x

我们只要把数字分为四类,就可以实现这个循环。x-x的分别是正负号,我们可以再利用数字的奇偶性,这两个正交属性可以产生4个组合。这个循环就可变成

正奇 正偶 负奇 负偶 正奇

可以看到,这个排列的正负号是每两次更改。接下来,就要想一个函数,满足这个变化。郑老师说他经过几次推敲,得到:

 

1.3  实现

在编程时,由于用(int)pow(-1,x)会做成浮点问题,所以我就改为:

view source

print?

01

template<typename t>

02

inline T even(T x) {

 

03

    return x % 2 == 0 ? -1 : 1;

04

}

 

05

  

06

template<typename t>

 

07

inline T sgn(T x) {

08

    if (x > 0)

 

09

        return 1;

10

    else if (x < 0)

 

11

        return -1;

12

    else

 

13

        return 0;

14

}

 

15

  

16

template<typename t>

 

17

struct f1 {

18

    T operator()(T x) {

 

19

        return even(x) * x + sgn(x);

20

    }

 

21

};

这个函数非常简单,可体现数学之美。郑老师也写了另一个比较少代码的实现:

view source

print?

01

template<typename T>

02

struct f2 {

 

03

    T operator()(T x) {

04

        if (x == 0)

 

05

            return 0;

06

        else if (x > 0)

 

07

            return x & 1 ? x + 1 : 1 - x;

08

        else

 

09

            return x & 1 ? x - 1 : -x - 1;

10

    }

 

11

};

1.4  测试

view source

print?

01

#include <limits>

02

#include <iostream>

 

03

  

04

using namespace std;

 

05

  

06

template<typename T, typename F>

 

07

void test(F& f) {

08

    cout << "[" << (int)numeric_limits<T>::min() << "," << (int)numeric_limits<T>::max() << "]" << endl;

 

09

    T x = numeric_limits<T>::min();

10

    do {

 

11

        T y = f(f(x));

12

        if (y != -x)

 

13

            cout << (int)x << " " << (int)y << endl;

14

        x++;

 

15

    } while (x != numeric_limits<T>::min());

16

  

 

17

    cout << endl;

18

}

 

19

  

20

void main() {

 

21

    test<signed char>(f1<signed char>());

22

    test<signed short>(f1<signed short>());

 

23

    test<int>(f1<int>());

24

  

 

25

    test<signed char>(f2<signed char>());

26

    test<signed short>(f2<signed short>());

 

27

    test<int>(f2<int>());

28

}

f1f2的执行结果相同:

view source

print?

1

[-128,127]

2

127 127

 

3

  

4

[-32768,32767]

 

5

32767 32767

6

  

 

7

[-2147483648,2147483647]

8

2147483647 2147483647

这结果说明,除了x为整数的上限时,结果正确。但因为没有额外的状态,相信这个边界问题应该不能解决。

1.5  第二题

第二题比较简单,只需要利用-(-x) = x的特点,无论x为正或负,经过这两次映射,总会有一次为正数,一次为负数。所以可以写一个函数,在x为正数时(或负数时)计算其倒数:

view source

print?

1

float g(float x) {

2

    return x > 0 ? -1.0f / x : -x;

 

3

}

这个函数在x=0时无定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值