【问题标题】:Factorial in C without conditionals, loops and arithmetic operators没有条件、循环和算术运算符的 C 中的阶乘
【发布时间】:2010-10-13 19:53:29
【问题描述】:

如何在 C 中找到数字(从 1 到 10)的阶乘,而不使用:

  • for、while 和 do while 等循环语句;
  • if 和 case 等条件运算符;和
  • 算术运算符,如 + 、 − 、 * 、 % 、 / 、 ++ 、 −−?

仅供参考:我在 C aptitude 中发现了这个问题。

【问题讨论】:

  • 您希望我们将答案直接通过电子邮件发送给您的教授吗?
  • @Paul - 谢谢你早上的笑声!
  • 您可以在您的问题中添加拼图标签吗?
  • 这个问题解决起来很有趣。你在哪里找到的?
  • 看过那个 C aptitude 网站...太可怕了!

标签: c algorithm puzzle


【解决方案1】:

既然没有说不要使用库函数:

#include    <stdlib.h>
#include    <stdio.h>
#include    <math.h>

int main( int argc, char** argv)
{
    printf( "%d\n", (int)round( exp( lgamma(2))));
    printf( "%d\n", (int)round( exp( lgamma(3))));
    printf( "%d\n", (int)round( exp( lgamma(4))));
    printf( "%d\n", (int)round( exp( lgamma(5))));
    printf( "%d\n", (int)round( exp( lgamma(6))));
    printf( "%d\n", (int)round( exp( lgamma(7))));
    printf( "%d\n", (int)round( exp( lgamma(8))));
    printf( "%d\n", (int)round( exp( lgamma(9))));
    printf( "%d\n", (int)round( exp( lgamma(10))));
    printf( "%d\n", (int)round( exp( lgamma(11))));

    return 0;
}

【讨论】:

    【解决方案2】:

    这是一个解决方案(目前唯一一个),它实际上解决了所需限制下的问题。

    int fac( int n )
    {
        /* The is the binary representation of the function: */
        /* 0000 => 0000000000000000001 */
        /* 0001 => 0000000000000000001 */
        /* 0010 => 0000000000000000010 */
        /* 0011 => 0000000000000000110 */
        /* 0100 => 0000000000000011000 */
        /* 0101 => 0000000000001111000 */
        /* 0110 => 0000000001011010000 */
        /* 0111 => 0000001001110110000 */
        /* 1000 => 0001001110110000000 */
        /* 1001 => 1011000100110000000 */
        int bit0 = n & 1;
        int bit1 = (n & 2) >> 1;
        int bit2 = (n & 4) >> 2;
        int bit3 = (n & 8) >> 3;
        int notbit0 = bit0 ^ 1;
        int notbit1 = bit1 ^ 1;
        int notbit2 = bit2 ^ 1;
        int notbit3 = bit3 ^ 1;
        return
        (bit0 & notbit1 & notbit2 & bit3) << 18 |
        (bit0 & notbit1 & notbit2 & bit3) << 16 |
        (notbit1 & notbit2 & bit3) << 15 |
        (notbit1 & notbit2 & bit3) << 11 |
        (notbit1 & notbit2 & bit3) << 8 |
        (notbit1 & notbit2 & bit3) << 7 |
        (notbit0 & notbit1 & notbit2 & bit3) << 12 |
        (notbit0 & notbit1 & notbit2 & bit3) << 10 |
        (bit0 & bit1 & bit2 & notbit3) << 12 |
        (bit1 & bit2 & notbit3) << 9 |
        (bit0 & bit1 & bit2 & notbit3) << 8 |
        (bit1 & bit2 & notbit3) << 7 |
        (bit0 & bit2 & notbit3) << 5 |
        (bit2 & notbit3) << 4 |
        (notbit0 & bit1 & bit2 & notbit3) << 6 |
        (bit0 & notbit1 & bit2 & notbit3) << 6 |
        (notbit1 & bit2 & notbit3) << 3 |    
        (bit0 & bit1 & notbit2 & notbit3) << 2 |    
        (bit1 & notbit2 & notbit3) << 1 |    
        (notbit1 & notbit2 & notbit3);
    }
    

    这是一个测试程序:

    #include <stdio.h>
    
    int main()
    {
        int i, expected, j;
        for( i = 0; i < 10; ++i )
        {
            expected = 1;
            for( j = 2; j <= i; ++j )
            {
                expected *= j;
            }
            if( expected != fac( i ) )
            {
                printf( "FAILED: fac(%d) = %d, expected %d\n", i, fac( i ), expected );
            }
        }
    }
    

    【讨论】:

    • 程序将阶乘函数(对于小于 10 的输入)编码为从作为位序列的输入到作为位序列的输出的映射。关键是只使用逻辑运算符。 (数组查找使用+)
    【解决方案3】:

    让我们看看我们是否可以在不依赖 1 的情况下做一些半优雅的事情。

    • 我们当然会使用递归来代替循环。
    • 我们将使用 函数指针数组,而不是 if 来终止递归!
      (我们仍然需要比较运算符,例如&lt;==。)

    编辑: damaru 首先使用了函数指针技巧。

    这给出:[所有代码都未经测试,手头没有 C 编译器!]

    typedef int (*unary_fptr)(int);
    
    int ret_1(int n) {
        return 1;
    }
    
    int fact(int n) {
        unary_fptr ret_1_or_fact[] = {ret_1, fact};
        return multiply(ret_1_or_fact[n > 1](sub_1(n)), n);
    }
    

    我们仍然需要实现sub_1multiply。让我们从sub_1开始,这是对位的简单递归,直到进位停止(如果你不明白这一点,最后类似的add_1更容易思考):

    int identity(int n) {
        return n;
    }
    
    int sub_1(int n) {
        unary_fptr sub_1_or_identity[] = {sub_1, identity};
        int lsb = n & 1;
        int rest = sub_1_or_identity[lsb](n >> 1);
        return (rest << 1) | (lsb ^ 1);
    }
    

    multiply:我能想到的最简单的是Russian Peasant multiplication,将其简化为二进制移位和加法。使用条件,递归公式如下所示:

     /* If we could use conditionals */
    int multiply(int a, int b) {
        int subproduct;
        if(a <= 1) {
           subproduct = 0;
        } else {
           subproduct = multiply(a >> 1, b << 1);
        }
    
        if(a & 1) {
           return add(b, subproduct);
        } else {
           return subproduct;
        }
    }
    

    没有条件,我们必须使用两次调度数组技巧:

    typedef int (*binary_fptr)(int, int);
    
    int ret_0(int a, int b) {
        return 0;
    }
    
    int multiply(int a, int b) {
        binary_fptr ret_0_or_multiply = {ret_0, multiply};
        int subproduct = ret_0_or_multiply[a >= 2](a >> 1, b << 1);
    
        binary_fptr ret_0_or_add = {ret_0, add};
        return ret_0_or_add[a & 1](subproduct, b);
    }
    

    现在我们想念的是add。您现在应该猜到它会如何进行 - 同时递归两个数字的位,这将问题减少到移位和add_1

    int add(int a, int b) {
        int lsb = (a & 1) ^ (b & 1);
        int carry = (a & 1) & (b & 1);
    
        binary_fptr ret_0_or_add = {ret_0, add};
        int subsum = ret_0_or_add[(a >= 2) & (b >= 2)](a >> 1, b>> 1);
    
        unary_fptr identity_or_add_1 = {identity, add_1};
        return identity_or_add_1[carry](subsum << 1);
    }
    

    add_1 是对位的简单递归,直到进位停止:

    int add_1(int n) {
        unary_fptr identity_or_add_1[] = {identity, add_1};
        int lsb = n & 1;
        int rest = identity_or_add_1[lsb](n >> 1);
        return (rest << 1) | (lsb ^ 1);
    }
    

    我想就是这样! [如上所述,所有代码都未经测试!]

    【讨论】:

      【解决方案4】:

      这是一个解决方案,它使用指针算术进行算术运算,将函数指针用于条件。

      #include <stdio.h>
      
      int fact(int n);
      
      int mul(int a, int b)
      {
              struct s {
                      char _v[b];
              };
              struct s *p = (struct s*)0;
              return (int) &p[a];
      }
      
      int add(int a, int b)
      {
              return (int) (&((char *)a)[b]);
      }
      
      int is_0(int n)
      {
              return (n == 0);
      }
      
      int fact_0(int n)
      {
              return 1;
      }
      
      int fact_n(int n)
      {
              return mul(n, fact(add(n,-1)));
      }
      
      int (*facts[2])(int) = {fact_n, fact_0};
      
      int fact(int n)
      {
              return facts[is_0(n)](n);
      }
      
      int main(int argc, char **argv)
      {
              int i;
              for(i = 0; i<=10; i++) {
                      printf("fact %d = %d\n", i, fact(i));
              }
      }
      

      示例运行:

       ~ > gcc -std=c99 fact.c 
       ~ > ./a.out 
      fact 0 = 1
      fact 1 = 1
      fact 2 = 2
      fact 3 = 6
      fact 4 = 24
      fact 5 = 120
      fact 6 = 720
      fact 7 = 5040
      fact 8 = 40320
      fact 9 = 362880
      fact 10 = 3628800
      

      【讨论】:

      • 好把戏,我从没想过指针是计算能力。
      【解决方案5】:

      这是一个没有循环、算术或条件的解决方案,它不诉诸预计算。 它也不使用像&amp;&amp;|| 这样的短路条件,它们实际上等同于if 所以这似乎是第一个完全没有任何条件的正确解决方案。现在在没有 C++ 特性的适当 C 中:)

      #include <stdio.h>
      #define uint unsigned int
      
      void A(uint *a, uint *b)
      {
          uint tmp = *a & *b;
          *a = (*a | *b) & ~tmp;
          *b = tmp << 1;
      }
      
      #define REPEAT32(s) \
      s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s
      
      uint add(uint a, uint b)
      {
          REPEAT32(A(&a, &b);) return a;
      }
      
      uint bitexpand(uint b)
      {
          b = (b << 1)  | b; b = (b << 2)  | b; b = (b << 4)  | b;
          b = (b << 8)  | b; b = (b << 16) | b;
          return b;
      }
      
      void M(uint *acc, uint *a, uint *b)
      {
          *acc = add(*acc, *a & bitexpand(*b & 1));
          *a <<= 1;
          *b >>= 1;
      }
      
      uint mult(uint a, uint b)
      {
          uint acc = 0;
          REPEAT32(M(&acc, &a, &b);) return acc;
      }
      
      uint factorial(int n)
      {
          uint k = 1;
          uint result = 0;
          result |= (bitexpand(n == 1) & k);
          k = mult(k, 2); result |= (bitexpand(n == 2) & k);
          k = mult(k, 3); result |= (bitexpand(n == 3) & k);
          k = mult(k, 4); result |= (bitexpand(n == 4) & k);
          k = mult(k, 5); result |= (bitexpand(n == 5) & k);
          k = mult(k, 6); result |= (bitexpand(n == 6) & k);
          k = mult(k, 7); result |= (bitexpand(n == 7) & k);
          k = mult(k, 8); result |= (bitexpand(n == 8) & k);
          k = mult(k, 9); result |= (bitexpand(n == 9) & k);
          k = mult(k, 10); result |= (bitexpand(n == 10) & k);
          return result;
      }
      
      int main(int argc, char **argv)
      {
          uint i;
          /* Demonstration loop, not part of solution */
          for (i = 1; i <= 10; i++)
          {
              printf("%d %d\n", i, factorial(i));
          }
      }
      

      更新:讨论包含这样的声明,即在不使用 if 的解决方案中,像 && 这样的短路条件是可以接受的。这是一个简单的宏,它使用 && 模拟双向“if”,显然使整个问题变得不那么有趣:

      #define IF(i, t, e) \
      (void)((i) && (goto then##__LINE__, 1)); goto else##__LINE__;
      then##__LINE__: t; goto cont##__LINE__; \
      else##__LINE__: e; cont##__LINE__: ((void)0);
      

      然后你可以定义

      #define WHILE(c, s) \
      loop##__LINE__: IF(c, s; goto loop##__LINE__, ((void)0)))
      

      然后剩下的问题就变得微不足道了。

      【讨论】:

      • 帖子说C,你这里是C++
      • 是的...但我们这里并没有真正使用 C++ 特性。好吧,让我解决它。
      • 您的 '==' 实际上是一个条件运算符。您可以通过 "add (n, -2)" 和 or'ing 结果位一起修复它:"bitexpand(or_all_bits(add(n,-2))^1)"
      • == 不是条件运算符,因为它不影响控制流,不像“if”、“while”和短路条件(||、&&)。它不会导致控制权转移。当然也可以用 XOR、OR 和移位来代替。
      • && 和 ||是逻辑运算符,与 >、=、== 和 != 属于同一类别。它们短路的事实只是一个优点。
      【解决方案6】:
      #include<stdio.h>
      void main()
      {
          unsigned long int num,fact,counter;
          while(counter<=num)
          {
              printf("Enter the number");
              scanf("%d",&num);
              fact=fact*counter;
              counter++;
              printf("The factorial of number entered is %lu",fact);
          }
          printf("press any key to exit...");
          getch();
      }
      

      【讨论】:

      • 标准要求'int main()';除非您知道您使用的是 C99,否则您应该从 main() 返回一个值 - 良好做法建议将 return 放入 main()。
      • 该死 - 社区 Wiki 条目给了我太多荣誉;我所做的只是添加空格。
      【解决方案7】:

      “+”、“-”和“*”是明确禁止的,但“+=”、“-=”和“*=”不是,所以递归实现变成……

      int factorial( int arg )
      {
          int argcopy = arg;
          argcopy -= 1;
          return arg == 1 ? arg : arg *= factorial( argcopy );
      }
      

      VC7 在“编译为 C 源代码模式”时拒绝编译上述内容——抱怨“*=”的 const L 值,但这里有另一个相同的变体:

      int factorial( int arg )
      {
          int argcopy1 = arg;
          int argcopy2 = arg;
          argcopy1 -= 1;
          argcopy2 *= arg == 1 ? 1 : fact( argcopy1 );
          return argcopy2;
      }
      

      【讨论】:

      • 不使用 ?: 违反 no ifs 规则?
      • @Ferruccio:与 = 违反 'No'' 规则的方式相同
      • 它在思想上确实违反了,但在形式上没有违反。如果你真的想避免这种操作,你必须使用预先计算好的数组和取值器。
      【解决方案8】:

      也许我正在解决某人的作业,但这看起来是一个有趣的挑战,无论如何,这是我的解决方案(编译时带有警告,但如果不让它看起来很丑(呃)就无法帮助那些人)

      编辑:我已经更改了程序,使其支持更长的阶乘(最多 20 个左右),并通过删除 prev() 中的查找表使代码更整洁。 p>

      #include <stdio.h>
      #include <stdlib.h>
      
      #define _if(CND, OP1, OP2) (((CND) && ((OP1) || 1)) || (OP2))
      
      long long int add(long long int x, long long int y){
          long long int r = x ^ y;
          long long int c = x & y;
              c = c << 1;    
          _if(c != 0, r = add(r, c), 1);
      
          return r;
      }
      
      long long int prev(long long int x){
          return add(x, -1);
      }                           
      
      long long int mult(long long int x, long long int y){
          long long int r;
      
          _if(x == 0,
               r = 0,
             _if(x == 1, 
                  r = y, 
                  r = add(y, mult(prev(x), y))));
      
          return r;
      }
      
      long long int fac(long long int x){
          long long int r;
      
          _if(x < 2,
              r = 1,
              r = mult(x, fac(prev(x))));
      
          return r;
      }
      
      int main(int argc, char**argv){
          long long int i;
      
          for(i = 0; i <= 20; i++)
              printf("factorial(%lli) => %lli\n", i, fac(i));
      
          return 0;
      }
      

      示例运行:

      [dsm@localhost:~/code/c]$ gcc -o proc proc.c
      [dsm@localhost:~/code/c]$ ./proc #/
      factorial(0) => 1
      factorial(1) => 1
      factorial(2) => 2
      factorial(3) => 6
      factorial(4) => 24
      factorial(5) => 120
      factorial(6) => 720
      factorial(7) => 5040
      factorial(8) => 40320
      factorial(9) => 362880
      factorial(10) => 3628800
      factorial(11) => 39916800
      factorial(12) => 479001600
      factorial(13) => 6227020800
      factorial(14) => 87178291200
      factorial(15) => 1307674368000
      factorial(16) => 20922789888000
      factorial(17) => 355687428096000
      factorial(18) => 6402373705728000
      factorial(19) => 121645100408832000
      factorial(20) => 2432902008176640000
      [dsm@localhost:~/code/c]$
      

      【讨论】:

      • 它使用与“if”等效的短路条件(&& 和 ||)(如程序本身所示)。请参阅我的答案以获得没有短路条件的答案。
      • && 和 ||是 LOGICAL 运算符,恰好具有短路特性。
      【解决方案9】:

      如果我们必须计算 1 到 100 的阶乘怎么办。如何存储这么大的数字?

      【讨论】:

      • 使用字符串,并且您必须编写自己的添加两个字符串的方法,无论大小(带有进位加法等)
      【解决方案10】:

      这不是一个完整的答案,只是add()mult() 函数的不同方法:

      #define add(a, b)  sizeof (struct { char x[a]; char y[b]; })
      #define mult(a, b) sizeof (struct { char x[a][b]; })
      

      (我相信 C 与 C++ 不同,它允许在 sizeof 中定义新类型。)

      这是基于指针算法的add() 的另一种(完全不可移植的)实现:

      int add(int x, int y) {
          return (int) &((char*) x)[y];
      }
      

      【讨论】:

      • 不确定 sizeof() 技巧是否适用于运行时已知的尺寸,但无论如何它完全是天才! +1。
      • 谢谢 :) 但是不,ab 必须在编译时知道 sizeof 调用才能工作(至少在标准 C 中)。
      【解决方案11】:

      由于它只有 1 到 10,因此只需预先计算它并将其存储在大小为 11 的简单 int 数组中。对于数组中的第一个元素,放入 1。它不是您的问题的有效输入范围,但也可以是正确的。

      我们需要存储 11 个元素而不是我们需要的 10 个,否则我们需要使用操作“-”来获取正确的索引。但是,您的问题中不允许减法。

      int factorial(int x)
      {
        return precomputedArray[x];
      }
      

      【讨论】:

      • 这也可以象征性地完成,不需要 precomp 数组(但很好的答案)
      • 实际上,我只是用谷歌搜索了它的“证明”,这很愚蠢。 0!只是未定义,但我猜它是 1 按照惯例。
      • @hanse j:是的,阶乘只是一个数学函数,如果 x = 0,则定义为 fact(x) = 1,否则 x * fact(x-1)
      • 嗯,这隐含地使用了 + 因为 arr[i] 是 *(arr + i) ;)
      • @Brian R. Bondy:实际上,阶乘定义为 n! = i 的乘积,其中 1
      【解决方案12】:
      #include <stdio.h>
      
      static const int factorial[] = {
          1,
          1,
          2,
          6,
          24,
          120,
          720,
          5040,
          40320,
          362880,
          3628800,
      };
      
      /* Test/demo program. */
      int main(void)
      {
          int i;
      
          for (i = 0; i <= 10; ++i)
              printf("%d %d\n", i, factorial[i]);
      
          return 0;
      }
      

      (使用此答案回答家庭作业问题的人要么失败,要么拥有幽默感强的老师。)

      (呸,我很慢。其他人已经给出了这个答案。请随意投票 回答。)

      【讨论】:

      • "for" 和 "++" 是不允许的。
      • 哎呀,那是在测试程序中
      • 0!根据阶乘的定义为 1。不是-1。 (这允许组合公式等在没有特殊情况的情况下工作。)
      • ...richard 并使其与 gamma 函数一致
      • 由于他们不能使用循环和 ++,只需剪切/粘贴 10 行,并将 'i' 替换为数字。技术上正确且聪明的 FTW。
      【解决方案13】:

      使用asm编写汇编代码。

      或者,预编译一个程序并从您的程序中执行它。

      你为什么要对你的代码施加这样的限制?

      【讨论】:

      • 汇编语言是不可移​​植的。
      • 它并没有说它必须是便携的。 (但是,它确实将其限制为 C)。
      • 他将其限制为 C,这意味着任何标准的 obidient 编译器都应该能够编译它。该标准不要求编译器能够在任何目标机器上编译任何汇编器。
      • 它在技术上是“C 语言”。你也可以制作非便携式纯 C。
      • 不,汇编程序不是“C 语言”。某些编译器可以接受 C 的某些超集,但任何使用内联 asm 的程序严格来说根本不是 C 程序。
      【解决方案14】:

      我也尝试将值放入数组中。 在这里,我使用了 if 条件和 while 循环,但不涉及算术运算符。!尝试我是否也可以删除它们。

      #include <stdio.h>
      
      int add(int a, int b)
      {
      int t1, t2, ab, bb, cb=0, orb=1, ans=0;
      
      do {
          t1 = a >> 1; 
          t2 = t1 << 1;
      
          if (a==t2) ab=0; else ab=1;
      
          t1 = b >> 1;
          t2 = t1 << 1; 
      
          if (b==t2) bb=0; else bb=1;
      
          if (ab==1 && bb==1) { 
              if (cb==1) ans=ans | orb; 
              cb = 1; 
              }
      
          if ( ab!=bb ) { 
              if (cb==0) ans = ans | orb; 
              }
      
          if (ab==0 && bb==0) {
              if (cb==1) { 
              ans = ans | orb;
              cb=0;
                      }
              }
      
          orb = orb << 1; 
          a = a >> 1;
          b = b >> 1;
      
          } while (a!=0 || b!=0);
      
      if (cb==1) ans = ans | orb;
      
      return ans;
      }
      
      
      
      int multiply(int x,int y)
      {
          int result = 0, i = 0 , j=0;
      
          while((i=add(i,1)) <= y)
              result = add(result,x);
      
          return result;
      
      }
      
      int factorial(int x)
      {
          if(x==1)
              return 1;
          else
              return multiply(x,factorial(x-1));
      
      }
      
      
      int main()
      {
          int x;
          printf("Enter a number between 0 and 10: ");
          scanf("%d" , &x);
          printf("\nFactorial: %d\n" , factorial(x));
          return 0;
      }
      

      【讨论】:

      • 是的,我已经提到过了。并尝试进一步完善它,使其满足这些约束。
      • 这违反了很多约束,它需要循环或递归。但是要进行递归,您需要有一个基本情况,这意味着您至少需要 1 个条件语句。
      • 对不起,我看到您在答案的顶部写了这个,然后删除了我的评论,就像您发布上述评论一样。
      【解决方案15】:

      粗略的草图(已经有人提出了!)

      int[] factorials = {1,1,2,6,24, 120,720, ..etc };
      return factorials[i];
      

      【讨论】:

        【解决方案16】:

        计算阶乘是您第一次(对许多人来说也是最后一次)使用递归。标准实现是

        long fact(int x)
        {
           if (x < 2)
             return 1L;
           else
             return fact(x - 1) * x;
        }
        

        有些人会争辩说最后一条语句应该是“x * fact(x-1)”,以便编译器可以识别它是尾递归。就个人而言,我怀疑任何编译器都足够聪明,能够以那种形式看到它,而不能以另一种形式看到它。

        但是,由于您已将其限制为不使用“if”或“-”,所以我不知道您会怎么做。

        【讨论】:

        • 我一开始也是这么写的,后来发现他不想用if语句
        • 失败。违反了他的任务的两个条件:p
        • 如果我错了,请纠正我,但 "x * fact(x-1)" 和 "fact(x-1) * x" 都不是尾递归的。举个例子,这样写出来:“int intermediate = fact(x-1); return intermediate * x;”您可以清楚地看到代码在递归调用之后做了额外的工作。
        • 公主说得对。此代码在任何两种形式中都不是尾递归的(但可以适当地重构)。哦,顺便说一句:GCC 编译器仍然会识别和优化上面的代码! Litb 在 SO 上的其他答案中发布了详细信息。
        • @Princess:函数不是尾递归的,因为递归调用后有乘法运算。使用尾递归,递归调用之后必须没有操作。为此,您需要将结果一直向上传递到基本情况,并在基本情况下返回它
        【解决方案17】:

        如果您不能使用递归或算术并且您的输入范围有限,您可以将结果硬编码为数组查找,

        所以:

        return factorials[x];
        

        您在factorials 中预先填写了相关值

        【讨论】:

          【解决方案18】:

          生成一组巨大的三元运算符,为每个允许的输入返回一个预先计算的值。使用宏来计算值。

          【讨论】:

          • 不幸的是,它看起来像?在禁止列表中
          • 当然,谁要求一个优雅的解决方案?
          • @cobbal 看起来?只是一个问号。在算术运算列表中看到它很奇怪。
          猜你喜欢
          • 1970-01-01
          • 2014-06-06
          • 1970-01-01
          • 1970-01-01
          • 2014-01-09
          • 2012-10-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多