(x == 0 || x == 1)을 단일 작업으로 단순화 할 수 있습니까?
그래서 저는 피보나치 수열 의 n 번째 숫자를 가능한 한 컴팩트 한 함수로 쓰려고했습니다 .
public uint fibn ( uint N )
{
return (N == 0 || N == 1) ? 1 : fibn(N-1) + fibn(N-2);
}
하지만 변경을 통해 이것을 더욱 간결하고 효율적으로 만들 수 있는지 궁금합니다.
(N == 0 || N == 1)
단일 비교로. 이것을 할 수있는 멋진 비트 시프트 연산이 있습니까?
이것도 작동합니다
Math.Sqrt(N) == N
0과 1의 제곱근은 각각 0과 1을 반환합니다.
비트 산술을 사용하여 산술 테스트를 구현하는 방법에는 여러 가지가 있습니다. 당신의 표현 :
x == 0 || x == 1
논리적으로 다음 각 항목과 동일합니다.
(x & 1) == x
(x & ~1) == 0
(x | 1) == 1
(~x | 1) == (uint)-1
x >> 1 == 0
보너스:
x * x == x
(증명에는 약간의 노력이 필요합니다)
그러나 실제로는 이러한 형식이 가장 읽기 쉽고 성능의 작은 차이는 실제로 비트 산술을 사용할 가치가 없습니다.
x == 0 || x == 1
x <= 1
(x
부호없는 정수 이기 때문에 )x < 2
(x
부호없는 정수 이기 때문에 )
인수가 uint
( unsigned ) 이므로 다음을 넣을 수 있습니다.
return (N <= 1) ? 1 : N * fibn(N-1);
가독성이 낮지 만 (IMHO) 각 문자를 세는 경우 ( 코드 골프 또는 유사)
return N < 2 ? 1 : N * fibn(N-1);
편집 : 편집 한 질문에 대해 :
return (N <= 1) ? 1 : fibn(N-1) + fibn(N-2);
또는
return N < 2 ? 1 : fibn(N-1) + fibn(N-2);
다음과 같이 다른 모든 비트가 0인지 확인할 수도 있습니다.
return (N & ~1) == 0 ? 1 : N * fibn(N-1);
Matt 덕분에 완전성을 위해 더 나은 솔루션 :
return (N | 1) == 1 ? 1 : N * fibn(N-1);
두 경우 모두 비트 연산자가.보다 우선 순위가 낮기 때문에 괄호를 처리해야합니다 ==
.
원하는 것이 함수를 더 효율적으로 만드는 것이라면 조회 테이블을 사용하십시오. 조회 테이블은 47 개 항목으로 놀라 울 정도로 작습니다. 다음 항목은 32 비트 부호없는 정수를 오버플로합니다. 물론이 함수는 작성하기가 수월합니다.
class Sequences
{
// Store the complete list of values that will fit in a 32-bit unsigned integer without overflow.
private static readonly uint[] FibonacciSequence = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418,
317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169,
63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073
};
public uint fibn(uint N)
{
return FibonacciSequence[N];
}
}
팩토리얼에 대해서도 동일한 작업을 수행 할 수 있습니다.
비트 시프트로 수행하는 방법
비트 시프트를 사용하고 코드를 다소 모호하게 만들고 싶다면 (짧게) 다음과 같이 할 수 있습니다.
public uint fibn ( uint N ) {
return N >> 1 != 0? fibn(N-1) + finb(N-2): 1;
}
N
언어 c 의 부호없는 정수의 N>>1
경우 하위 비트를 버립니다. 그 결과가 0이 아니면 N이 1보다 크다는 것을 의미합니다.
참고 :이 알고리즘은 이미 계산 된 시퀀스의 값을 불필요하게 다시 계산하므로 매우 비효율적입니다.
더 빠른 방법
fibonaci (N) 크기의 트리를 암시 적으로 구축하는 대신 한 번의 패스를 계산합니다.
uint faster_fibn(uint N) { //requires N > 1 to work
uint a = 1, b = 1, c = 1;
while(--N != 0) {
c = b + a;
a = b;
b = c;
}
return c;
}
일부 사람들이 언급했듯이 64 비트 부호없는 정수도 오버플로하는 데 오래 걸리지 않습니다. 얼마나 큰지에 따라 임의의 정밀도 정수를 사용해야합니다.
음수가 될 수없는 단위를 사용하면 다음 사항을 확인할 수 있습니다. n < 2
편집하다
또는 특수한 기능의 경우 다음과 같이 작성할 수 있습니다.
public uint fibn(uint N)
return (N == 0) ? 1 : N * fibn(N-1);
}
물론 추가 재귀 단계의 비용으로 동일한 결과를 얻을 수 있습니다.
N
N이 서명되지 않았 음을 알기 때문에 <= 1 인지 확인하기 만하면 N <= 1
됩니다 TRUE
. 결과는 0과 1입니다.
public uint fibn ( uint N )
{
return (N <= 1) ? 1 : fibn(N-1) + finb(N-2);
}
면책 조항 : C #을 모르고이 코드를 테스트하지 않았습니다.
하지만 [...]를 단일 비교로 변경하여 더 간결하고 효율적으로 만들 수 있는지 궁금합니다.
비트 시프 팅 등의 필요가 없습니다. 이것은 하나의 비교 만 사용하며 훨씬 더 효율적 이어야합니다 (O (n) 대 O (2 ^ n) 제 생각에는?). 함수의 본문은 더 간결 하지만 선언과 함께 조금 더 길어집니다.
(재귀에서 오버 헤드를 제거하기 위해 Mathew Gunn의 답변 에서와 같이 반복 버전이 있습니다. )
public uint fibn ( uint N, uint B=1, uint A=0 )
{
return N == 0 ? A : fibn( N--, A+B, B );
}
fibn( 5 ) =
fibn( 5, 1, 0 ) =
return 5 == 0 ? 0 : fibn( 5--, 0+1, 1 ) =
fibn( 4, 1, 1 ) =
return 4 == 0 ? 1 : fibn( 4--, 1+1, 1 ) =
fibn( 3, 2, 1 ) =
return 3 == 0 ? 1 : fibn( 3--, 1+2, 2 ) =
fibn( 2, 3, 2 ) =
return 2 == 0 ? 2 : fibn( 2--, 2+3, 3 ) =
fibn( 1, 5, 3 ) =
return 1 == 0 ? 3 : fibn( 1--, 3+5, 5 ) =
fibn( 0, 8, 5 ) =
return 0 == 0 ? 5 : fibn( 0--, 5+8, 8 ) =
5
fibn(5)=5
PS: This is a common functional pattern for iteration with accumulators. If you replace N--
with N-1
you're effectively using no mutation, which makes it usable in a pure functional approach.
Here's my solution, there's not much in optimizing this simple function, on the other hand what I'm offering here is readability as a mathematical definition of the recursive function.
public uint fibn(uint N)
{
switch(N)
{
case 0: return 1;
case 1: return 1;
default: return fibn(N-1) + fibn(N-2);
}
}
The mathematical definition of Fibonacci number in a similar fashion..
Taking it further to force the switch case to build a lookup table.
public uint fibn(uint N)
{
switch(N)
{
case 0: return 1;
case 1: return 1;
case 2: return 2;
case 3: return 3;
case 4: return 5;
case 5: return 8;
case 6: return 13;
case 7: return 21;
case 8: return 34;
case 9: return 55;
case 10: return 89;
case 11: return 144;
case 12: return 233;
case 13: return 377;
case 14: return 610;
case 15: return 987;
case 16: return 1597;
case 17: return 2584;
case 18: return 4181;
case 19: return 6765;
case 20: return 10946;
case 21: return 17711;
case 22: return 28657;
case 23: return 46368;
case 24: return 75025;
case 25: return 121393;
case 26: return 196418;
case 27: return 317811;
case 28: return 514229;
case 29: return 832040;
case 30: return 1346269;
case 31: return 2178309;
case 32: return 3524578;
case 33: return 5702887;
case 34: return 9227465;
case 35: return 14930352;
case 36: return 24157817;
case 37: return 39088169;
case 38: return 63245986;
case 39: return 102334155;
case 40: return 165580141;
case 41: return 267914296;
case 42: return 433494437;
case 43: return 701408733;
case 44: return 1134903170;
case 45: return 1836311903;
case 46: return 2971215073;
default: return fibn(N-1) + fibn(N-2);
}
}
for N is uint, just use
N <= 1
Dmitry's answer is best but if it was an Int32 return type and you had a larger set of integers to choose from you could do this.
return new List<int>() { -1, 0, 1, 2 }.Contains(N) ? 1 : N * fibn(N-1);
The Fibonacci sequence is a series of numbers where a number is found by adding up the two numbers before it. There are two types of starting points: (0,1,1,2,..) and (1,1,2,3).
-----------------------------------------
Position(N)| Value type 1 | Value type 2
-----------------------------------------
1 | 0 | 1
2 | 1 | 1
3 | 1 | 2
4 | 2 | 3
5 | 3 | 5
6 | 5 | 8
7 | 8 | 13
-----------------------------------------
The position N
in this case starts from 1
, it is not 0-based
as an array index.
Using C# 6 Expression-body feature and Dmitry's suggestion about ternary operator we can write a one line function with correct calculation for the type 1:
public uint fibn(uint N) => N<3? N-1: fibn(N-1)+fibn(N-2);
and for the type 2:
public uint fibn(uint N) => N<3? 1: fibn(N-1)+fibn(N-2);
Bit late to the party, but you could also do (x==!!x)
!!x
converts the a value to 1
if it's not 0
, and leaves it at 0
if it is.
I use this kinda thing in C obfuscation a lot.
Note: This is C, not sure if it works in C#
So I created a List
of these special integers and checked if N
pertains to it.
static List<uint> ints = new List<uint> { 0, 1 };
public uint fibn(uint N)
{
return ints.Contains(N) ? 1 : fibn(N-1) + fibn(N-2);
}
You could also use an extension method for different purposes where Contains
is called only once (e. g. when your application is starting and loading data). This provides a clearer style and clarifies the primary relation to your value (N
):
static class ObjectHelper
{
public static bool PertainsTo<T>(this T obj, IEnumerable<T> enumerable)
{
return (enumerable is List<T> ? (List<T>) enumerable : enumerable.ToList()).Contains(obj);
}
}
Apply it:
N.PertainsTo(ints)
This might be not the fastest way to do it, but to me, it appears to be a better style.
'developer tip' 카테고리의 다른 글
Android에서 TextView 아래에 밑줄을 그리려면 (0) | 2020.08.08 |
---|---|
.NET / Mono 또는 Java가 크로스 플랫폼 개발에 더 적합한 선택입니까? (0) | 2020.08.08 |
세 번째 열을 마지막 열로 인쇄하는 방법은 무엇입니까? (0) | 2020.08.08 |
XML 문자열을 사전으로 변환하는 방법은 무엇입니까? (0) | 2020.08.08 |
고정 된 PostgreSQL 데이터베이스 백업 / 복원 (0) | 2020.08.08 |