Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 days ago.
Improve this question
I just made a program that creates a deck with 26 cards, splits it into 5 hands of 5 cards (discarding 1) and checks the poker combinations that are in those hands, if any.
Now, I also made another program that loops over this until a Royal Flush is found (which is very rare and should happen, on average, once every 600k or so decks).
And the strange thing is, once I added a counter to show how many decks it went through, it only said 150 - 4000 ! And I think it's the random shuffler's fault. I had made a similar program in Python, and that was checking approximately the correct amount of decks.
I used this, which should shuffle the deck in place:
fn shuffle_deck(deck: &mut Vec<Card>) -> () {
deck.shuffle(&mut rand::thread_rng())
}
Apparently, it's not doing a very good job at being random. Can anybody help me in finding a better solution?
Edit: also, for anyone wondering, this is the Card struct:
pub struct Card {
value: i32,
suit: String
}
Your assumption is incorrect.
26 cards can make 26 over 5 possible combinations, which is 65780 combinations.
As you have 4 royal flushes in your deck, the probability to get dealt a royal flush is 4/65780 = 0.006080875646093037 % or one out of ever 16445.
If you look at four at a time, this number roughly divides by 4 (not exactly because those four draws aren't independent), so you should get something in the ballpark of one every ~3300.
So if you measure it experimentally, you get:
use rand::seq::SliceRandom;
fn is_royal_flush(deck: &[u8]) -> bool {
if deck.len() != 5 {
false
} else {
let mut buckets = [0u8; 4];
for el in deck {
if let Some(bucket) = buckets.get_mut((*el / 5) as usize) {
*bucket += 1;
}
}
buckets.iter().any(|bucket| *bucket == 5)
}
}
fn has_royal_flush(deck: &[u8; 26]) -> bool {
is_royal_flush(&deck[0..5])
|| is_royal_flush(&deck[5..10])
|| is_royal_flush(&deck[10..15])
|| is_royal_flush(&deck[15..20])
|| is_royal_flush(&deck[20..25])
}
fn main() {
let mut rng = rand::thread_rng();
let mut deck = [0; 26];
deck.iter_mut()
.enumerate()
.for_each(|(pos, val)| *val = pos as u8);
println!("Deck, initially: {:?}", deck);
deck.shuffle(&mut rng);
println!("Deck, shuffled: {:?}", deck);
println!();
let mut total: usize = 0;
let mut royal_flushes: usize = 0;
loop {
deck.shuffle(&mut rng);
total += 1;
if has_royal_flush(&deck) {
royal_flushes += 1;
}
if total % 10000000 == 0 {
println!(
"Now {} out of {}. (Probability: 1/{})",
royal_flushes,
total,
total / royal_flushes
);
}
}
}
Deck, initially: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
Deck, shuffled: [25, 16, 3, 12, 18, 1, 14, 8, 17, 0, 5, 15, 6, 11, 4, 21, 2, 13, 24, 20, 22, 7, 10, 9, 19, 23]
Now 2976 out of 10000000. (Probability: 1/3360)
Now 6004 out of 20000000. (Probability: 1/3331)
Now 8973 out of 30000000. (Probability: 1/3343)
Now 11984 out of 40000000. (Probability: 1/3337)
Which is roughly in the expected ballpark. So your assumption is off that you should get only one royal flush every 600k, and most likely something is wrong with your python code.
That said, if you compute the probability to get a single royal flush in a 52 card deck, then you should get (52 over 5) / 4 = one every 649740 draws. Which is probably what you were referring to, and if you program it, it matches its expectations:
use rand::seq::SliceRandom;
fn is_royal_flush(deck: &[u8]) -> bool {
if deck.len() != 5 {
false
} else {
let mut buckets = [0u8; 4];
for el in deck {
if let Some(bucket) = buckets.get_mut((*el / 5) as usize) {
*bucket += 1;
}
}
buckets.iter().any(|bucket| *bucket == 5)
}
}
fn has_royal_flush(deck: &[u8; 52]) -> bool {
is_royal_flush(&deck[0..5])
}
fn main() {
let mut rng = rand::thread_rng();
let mut deck = [0; 52];
deck.iter_mut()
.enumerate()
.for_each(|(pos, val)| *val = pos as u8);
println!("Deck, initially: {:?}", deck);
deck.shuffle(&mut rng);
println!("Deck, shuffled: {:?}", deck);
println!();
let mut total: usize = 0;
let mut royal_flushes: usize = 0;
loop {
deck.shuffle(&mut rng);
total += 1;
if has_royal_flush(&deck) {
royal_flushes += 1;
}
if total % 10000000 == 0 {
println!(
"Now {} out of {}. (Probability: 1/{})",
royal_flushes,
total,
total / royal_flushes
);
}
}
}
Deck, initially: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
Deck, shuffled: [2, 15, 44, 39, 26, 47, 28, 11, 1, 19, 31, 6, 43, 42, 29, 48, 35, 30, 3, 49, 50, 37, 9, 10, 18, 45, 33, 22, 36, 5, 38, 46, 51, 32, 7, 17, 23, 27, 41, 14, 21, 13, 25, 4, 8, 16, 20, 24, 12, 34, 0, 40]
Now 6 out of 10000000. (Probability: 1/1666666)
Now 22 out of 20000000. (Probability: 1/909090)
Now 36 out of 30000000. (Probability: 1/833333)
Now 54 out of 40000000. (Probability: 1/740740)
Now 70 out of 50000000. (Probability: 1/714285)
Now 78 out of 60000000. (Probability: 1/769230)
Now 92 out of 70000000. (Probability: 1/760869)
Now 102 out of 80000000. (Probability: 1/784313)
Now 125 out of 90000000. (Probability: 1/720000)
Now 139 out of 100000000. (Probability: 1/719424)
Now 164 out of 110000000. (Probability: 1/670731)
Now 190 out of 120000000. (Probability: 1/631578)
Now 202 out of 130000000. (Probability: 1/643564)
Now 217 out of 140000000. (Probability: 1/645161)
Now 232 out of 150000000. (Probability: 1/646551)
Now 248 out of 160000000. (Probability: 1/645161)
If I have an array A = <0, 15, 5, 1, 0, 20, 25, 30, 35, 40>. When i write the code to count the comparisons, I am confused on where to add a counter, because I'm afraid there might be repeated counts.
Nevertheless, it says there are 15 comparisons. I am not sure this is right. How many comparisons are there really?
int InsertionSort(int A[], int n)
{
int i, j, index, counter = 0;
for (i=1; i < n; i++)
{
index = A[i];
for (j=i-1;j >= 0 && A[j] > index;j--)
{
A[j + 1] = A[j];
counter++;
}
A[j+1] = index;
counter++;
}
return counter;
}
int main()
{
int A[]= {5,4,3,2,1};
int counter = 0;
int n =5;
counter = InsertionSort(A, n);
printf("%d",counter);
return 0;
}
There are 15 comparisons (and 6 swaps):
compare: 0 <= 15, 5, 1, 0, 20, 25, 30, 35, 40
compare: 0, 15 > 5, 1, 0, 20, 25, 30, 35, 40
swap: 0, 5 - 15, 1, 0, 20, 25, 30, 35, 40
compare: 0 <= 5, 15, 1, 0, 20, 25, 30, 35, 40
compare: 0, 5, 15 > 1, 0, 20, 25, 30, 35, 40
swap: 0, 5, 1 - 15, 0, 20, 25, 30, 35, 40
compare: 0, 5 > 1, 15, 0, 20, 25, 30, 35, 40
swap: 0, 1 - 5, 15, 0, 20, 25, 30, 35, 40
compare: 0 <= 1, 5, 15, 0, 20, 25, 30, 35, 40
compare: 0, 1, 5, 15 > 0, 20, 25, 30, 35, 40
swap: 0, 1, 5, 0 - 15, 20, 25, 30, 35, 40
compare: 0, 1, 5 > 0, 15, 20, 25, 30, 35, 40
swap: 0, 1, 0 - 5, 15, 20, 25, 30, 35, 40
compare: 0, 1 > 0, 5, 15, 20, 25, 30, 35, 40
swap: 0, 0 - 1, 5, 15, 20, 25, 30, 35, 40
compare: 0 <= 0, 1, 5, 15, 20, 25, 30, 35, 40
compare: 0, 0, 1, 5, 15 <= 20, 25, 30, 35, 40
compare: 0, 0, 1, 5, 15, 20 <= 25, 30, 35, 40
compare: 0, 0, 1, 5, 15, 20, 25 <= 30, 35, 40
compare: 0, 0, 1, 5, 15, 20, 25, 30 <= 35, 40
compare: 0, 0, 1, 5, 15, 20, 25, 30, 35 <= 40
To me, your counter appears to be on the wrong spot. Let's say A=<3, 2>, then your algorithm would use 1 comparison, but would report counter=2. If 15 is the right answer, then this error did not occur or got canceled out somehow.
To find out if 15 really is the right answer, this is how you can improve the counter. First of all, your algorithm relies on a left-to-right evaluation order of conditions (which most programming language adhere to). What this means is that if P=false then Q is not evaluated in (P && Q). If left-to-right evaluation order is not guaranteed, then the algorithm could potentially evaluate A[-1] > index (something which would crash your program). The easiest way to count correctly is to split the conjunction of the for-loop into two separate lines as follows:
for (i=1; i < n; i++)
{
index = A[i];
for (j=i-1; j >= 0; j--)
{
// Every time this line is reached, a comparison will be performed
counter++;
if (A[j] > index)
{
A[j + 1] = A[j];
}
}
A[j+1] = index;
}
If this works out, please let us know the result and please up-vote this answer.
I'm building an application that will tell your Chinese sign. I looked around but only found charts (from 1900 to 2020), and no logic to create something more dynamic.
Is there no logic for determining a Chinese zodiac?
Does that answer your question:
public string ChineseZodiac(System.DateTime date)
{
System.Globalization.EastAsianLunisolarCalendar cc =
new System.Globalization.ChineseLunisolarCalendar();
int sexagenaryYear = cc.GetSexagenaryYear(date);
int terrestrialBranch = cc.GetTerrestrialBranch(sexagenaryYear);
// string[] years = "rat,ox,tiger,hare,dragon,snake,horse,sheep,monkey,fowl,dog,pig".Split(',');
// string[] years = "Rat,Ox,Tiger,Rabbit,Dragon,Snake,Horse,Goat,Monkey,Rooster,Dog,Pig".Split(',');
// string[] years = new string[]{ "rat", "ox", "tiger", "hare", "dragon", "snake", "horse", "sheep", "monkey", "fowl", "dog", "pig" };
string[] years = new string[]{ "Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig" };
return years[terrestrialBranch - 1];
} // End Function ChineseZodiac
Fore those that need the source code for another programming language:
I've ripped the corresponding sources out of .NET Framwork, here:
https://gist.github.com/ststeiger/709354299a457e2d79b06d0127096fee
Edit:
I ported this to JavaScript:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChineseZodiac = void 0;
var DateTimeKind;
(function (DateTimeKind) {
DateTimeKind[DateTimeKind["Unspecified"] = 0] = "Unspecified";
DateTimeKind[DateTimeKind["Utc"] = 1] = "Utc";
DateTimeKind[DateTimeKind["Local"] = 2] = "Local";
})(DateTimeKind || (DateTimeKind = {}));
var GregorianCalendar = (function () {
function GregorianCalendar() {
}
GregorianCalendar.prototype.GetYear = function (time) {
var ticks = time.JavaScriptTicks;
var dt = new Date(ticks);
return dt.getUTCFullYear();
};
GregorianCalendar.prototype.GetMonth = function (time) {
var ticks = time.JavaScriptTicks;
var dt = new Date(ticks);
return (dt.getUTCMonth() + 1);
};
GregorianCalendar.prototype.GetDayOfMonth = function (time) {
var ticks = time.JavaScriptTicks;
var dt = new Date(ticks);
return dt.getUTCDate();
};
return GregorianCalendar;
}());
var DotNetDateTime = (function () {
function DotNetDateTime(ticks, dt) {
this.m_ticks = 0;
this.m_ticks = ticks;
}
DotNetDateTime.fromJsDate = function (dt) {
var jsTicks = dt.getTime();
var dotNetJsbaseTicks = 621355968000000000;
var tenK = 10000;
var dotTicks = dotNetJsbaseTicks + jsTicks * tenK;
return new DotNetDateTime(dotTicks, DateTimeKind.Unspecified);
};
Object.defineProperty(DotNetDateTime.prototype, "Ticks", {
get: function () {
return this.m_ticks;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DotNetDateTime.prototype, "JavaScriptTicks", {
get: function () {
var dotNetJsbaseTicks = 621355968000000000;
var dotNetTicksSince1970 = this.m_ticks - dotNetJsbaseTicks;
var jsTicks = parseInt((dotNetTicksSince1970 / 10000).toString(), 10);
return jsTicks;
},
enumerable: false,
configurable: true
});
return DotNetDateTime;
}());
var MinLunisolarYear = 1901;
var MaxLunisolarYear = 2100;
var DaysPerYear = 365;
var DaysPer4Years = DaysPerYear * 4 + 1;
var DaysPer100Years = DaysPer4Years * 25 - 1;
var DaysPer400Years = DaysPer100Years * 4 + 1;
var DaysTo10000 = DaysPer400Years * 25 - 366;
var TicksPerMillisecond = 10000;
var TicksPerSecond = TicksPerMillisecond * 1000;
var TicksPerMinute = TicksPerSecond * 60;
var TicksPerHour = TicksPerMinute * 60;
var TicksPerDay = TicksPerHour * 24;
var MinTicks = 0;
var MaxTicks = DaysTo10000 * TicksPerDay - 1;
var MinValue = new DotNetDateTime(MinTicks, DateTimeKind.Unspecified);
var MaxValue = new DotNetDateTime(MaxTicks, DateTimeKind.Unspecified);
var Jan1Month = 1;
var Jan1Date = 2;
var nDaysPerMonth = 3;
var s_daysToMonth365 = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
var s_daysToMonth366 = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
var s_yinfo = [
[0, 2, 19, 19168],
[0, 2, 8, 42352],
[5, 1, 29, 21096],
[0, 2, 16, 53856],
[0, 2, 4, 55632],
[4, 1, 25, 27304],
[0, 2, 13, 22176],
[0, 2, 2, 39632],
[2, 1, 22, 19176],
[0, 2, 10, 19168],
[6, 1, 30, 42200],
[0, 2, 18, 42192],
[0, 2, 6, 53840],
[5, 1, 26, 54568],
[0, 2, 14, 46400],
[0, 2, 3, 54944],
[2, 1, 23, 38608],
[0, 2, 11, 38320],
[7, 2, 1, 18872],
[0, 2, 20, 18800],
[0, 2, 8, 42160],
[5, 1, 28, 45656],
[0, 2, 16, 27216],
[0, 2, 5, 27968],
[4, 1, 24, 44456],
[0, 2, 13, 11104],
[0, 2, 2, 38256],
[2, 1, 23, 18808],
[0, 2, 10, 18800],
[6, 1, 30, 25776],
[0, 2, 17, 54432],
[0, 2, 6, 59984],
[5, 1, 26, 27976],
[0, 2, 14, 23248],
[0, 2, 4, 11104],
[3, 1, 24, 37744],
[0, 2, 11, 37600],
[7, 1, 31, 51560],
[0, 2, 19, 51536],
[0, 2, 8, 54432],
[6, 1, 27, 55888],
[0, 2, 15, 46416],
[0, 2, 5, 22176],
[4, 1, 25, 43736],
[0, 2, 13, 9680],
[0, 2, 2, 37584],
[2, 1, 22, 51544],
[0, 2, 10, 43344],
[7, 1, 29, 46248],
[0, 2, 17, 27808],
[0, 2, 6, 46416],
[5, 1, 27, 21928],
[0, 2, 14, 19872],
[0, 2, 3, 42416],
[3, 1, 24, 21176],
[0, 2, 12, 21168],
[8, 1, 31, 43344],
[0, 2, 18, 59728],
[0, 2, 8, 27296],
[6, 1, 28, 44368],
[0, 2, 15, 43856],
[0, 2, 5, 19296],
[4, 1, 25, 42352],
[0, 2, 13, 42352],
[0, 2, 2, 21088],
[3, 1, 21, 59696],
[0, 2, 9, 55632],
[7, 1, 30, 23208],
[0, 2, 17, 22176],
[0, 2, 6, 38608],
[5, 1, 27, 19176],
[0, 2, 15, 19152],
[0, 2, 3, 42192],
[4, 1, 23, 53864],
[0, 2, 11, 53840],
[8, 1, 31, 54568],
[0, 2, 18, 46400],
[0, 2, 7, 46752],
[6, 1, 28, 38608],
[0, 2, 16, 38320],
[0, 2, 5, 18864],
[4, 1, 25, 42168],
[0, 2, 13, 42160],
[10, 2, 2, 45656],
[0, 2, 20, 27216],
[0, 2, 9, 27968],
[6, 1, 29, 44448],
[0, 2, 17, 43872],
[0, 2, 6, 38256],
[5, 1, 27, 18808],
[0, 2, 15, 18800],
[0, 2, 4, 25776],
[3, 1, 23, 27216],
[0, 2, 10, 59984],
[8, 1, 31, 27432],
[0, 2, 19, 23232],
[0, 2, 7, 43872],
[5, 1, 28, 37736],
[0, 2, 16, 37600],
[0, 2, 5, 51552],
[4, 1, 24, 54440],
[0, 2, 12, 54432],
[0, 2, 1, 55888],
[2, 1, 22, 23208],
[0, 2, 9, 22176],
[7, 1, 29, 43736],
[0, 2, 18, 9680],
[0, 2, 7, 37584],
[5, 1, 26, 51544],
[0, 2, 14, 43344],
[0, 2, 3, 46240],
[4, 1, 23, 46416],
[0, 2, 10, 44368],
[9, 1, 31, 21928],
[0, 2, 19, 19360],
[0, 2, 8, 42416],
[6, 1, 28, 21176],
[0, 2, 16, 21168],
[0, 2, 5, 43312],
[4, 1, 25, 29864],
[0, 2, 12, 27296],
[0, 2, 1, 44368],
[2, 1, 22, 19880],
[0, 2, 10, 19296],
[6, 1, 29, 42352],
[0, 2, 17, 42208],
[0, 2, 6, 53856],
[5, 1, 26, 59696],
[0, 2, 13, 54576],
[0, 2, 3, 23200],
[3, 1, 23, 27472],
[0, 2, 11, 38608],
[11, 1, 31, 19176],
[0, 2, 19, 19152],
[0, 2, 8, 42192],
[6, 1, 28, 53848],
[0, 2, 15, 53840],
[0, 2, 4, 54560],
[5, 1, 24, 55968],
[0, 2, 12, 46496],
[0, 2, 1, 22224],
[2, 1, 22, 19160],
[0, 2, 10, 18864],
[7, 1, 30, 42168],
[0, 2, 17, 42160],
[0, 2, 6, 43600],
[5, 1, 26, 46376],
[0, 2, 14, 27936],
[0, 2, 2, 44448],
[3, 1, 23, 21936],
[0, 2, 11, 37744],
[8, 2, 1, 18808],
[0, 2, 19, 18800],
[0, 2, 8, 25776],
[6, 1, 28, 27216],
[0, 2, 15, 59984],
[0, 2, 4, 27296],
[4, 1, 24, 43872],
[0, 2, 12, 43744],
[0, 2, 2, 37600],
[3, 1, 21, 51568],
[0, 2, 9, 51552],
[7, 1, 29, 54440],
[0, 2, 17, 54432],
[0, 2, 5, 55888],
[5, 1, 26, 23208],
[0, 2, 14, 22176],
[0, 2, 3, 42704],
[4, 1, 23, 21224],
[0, 2, 11, 21200],
[8, 1, 31, 43352],
[0, 2, 19, 43344],
[0, 2, 7, 46240],
[6, 1, 27, 46416],
[0, 2, 15, 44368],
[0, 2, 5, 21920],
[4, 1, 24, 42448],
[0, 2, 12, 42416],
[0, 2, 2, 21168],
[3, 1, 22, 43320],
[0, 2, 9, 26928],
[7, 1, 29, 29336],
[0, 2, 17, 27296],
[0, 2, 6, 44368],
[5, 1, 26, 19880],
[0, 2, 14, 19296],
[0, 2, 3, 42352],
[4, 1, 24, 21104],
[0, 2, 10, 53600],
[8, 1, 30, 59696],
[0, 2, 18, 54560],
[0, 2, 7, 55968],
[6, 1, 27, 27472],
[0, 2, 15, 22224],
[0, 2, 5, 19168],
[4, 1, 25, 42216],
[0, 2, 12, 41680],
[0, 2, 1, 53584],
[2, 1, 21, 55592],
[0, 2, 9, 54560],
];
function GregorianIsLeapYear(y) {
if ((y % 4) != 0) {
return false;
}
if ((y % 100) != 0) {
return true;
}
return (y % 400) == 0;
}
function GetYearInfo(lunarYear, index) {
if (lunarYear < MinLunisolarYear || lunarYear > MaxLunisolarYear) {
throw new Error("year");
}
return s_yinfo[lunarYear - MinLunisolarYear][index];
}
function CheckTicksRange(ticks) {
if (ticks < MinValue.Ticks || ticks > MaxValue.Ticks) {
throw new Error("time");
}
}
function GregorianToLunar(solarYear, solarMonth, solarDate) {
var outData = { lunarYear: 0, lunarMonth: 0, lunarDate: 0 };
var isLeapYear = GregorianIsLeapYear(solarYear);
var jan1Month;
var jan1Date;
var solarDay = isLeapYear ? s_daysToMonth366[solarMonth - 1] : s_daysToMonth365[solarMonth - 1];
solarDay += solarDate;
var lunarDay = solarDay;
outData.lunarYear = solarYear;
if (outData.lunarYear == (MaxLunisolarYear + 1)) {
outData.lunarYear--;
lunarDay += (GregorianIsLeapYear(outData.lunarYear) ? 366 : 365);
jan1Month = GetYearInfo(outData.lunarYear, Jan1Month);
jan1Date = GetYearInfo(outData.lunarYear, Jan1Date);
}
else {
jan1Month = GetYearInfo(outData.lunarYear, Jan1Month);
jan1Date = GetYearInfo(outData.lunarYear, Jan1Date);
if ((solarMonth < jan1Month) ||
(solarMonth == jan1Month && solarDate < jan1Date)) {
outData.lunarYear--;
lunarDay += (GregorianIsLeapYear(outData.lunarYear) ? 366 : 365);
jan1Month = GetYearInfo(outData.lunarYear, Jan1Month);
jan1Date = GetYearInfo(outData.lunarYear, Jan1Date);
}
}
lunarDay -= s_daysToMonth365[jan1Month - 1];
lunarDay -= (jan1Date - 1);
var mask = 0x8000;
var yearInfo = GetYearInfo(outData.lunarYear, nDaysPerMonth);
var days = ((yearInfo & mask) != 0) ? 30 : 29;
outData.lunarMonth = 1;
while (lunarDay > days) {
lunarDay -= days;
outData.lunarMonth++;
mask >>= 1;
days = ((yearInfo & mask) != 0) ? 30 : 29;
}
outData.lunarDate = lunarDay;
return outData;
}
function TimeToLunar(time) {
var gregorianCalendar = new GregorianCalendar();
var gy = gregorianCalendar.GetYear(time);
var gm = gregorianCalendar.GetMonth(time);
var gd = gregorianCalendar.GetDayOfMonth(time);
var ad = GregorianToLunar(gy, gm, gd);
return {
year: ad.lunarYear
};
}
function GetSexagenaryYear(time) {
CheckTicksRange(time.Ticks);
var x = TimeToLunar(time);
return ((x.year - 4) % 60) + 1;
}
function GetTerrestrialBranch(sexagenaryYear) {
if (sexagenaryYear < 1 || sexagenaryYear > 60) {
throw new Error("sexagenaryYear");
}
return ((sexagenaryYear - 1) % 12) + 1;
}
function ChineseZodiac(date) {
var dotNetDate = DotNetDateTime.fromJsDate(date);
var sexagenaryYear = GetSexagenaryYear(dotNetDate);
var terrestrialBranch = GetTerrestrialBranch(sexagenaryYear);
var years = ["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"];
return years[terrestrialBranch - 1];
}
exports.ChineseZodiac = ChineseZodiac;
Testing:
var exports = {};
// [copy-pasting above code]
exports.ChineseZodiac(new Date(1970,0,1))
exports.ChineseZodiac(new Date(2021,0,1))
exports.ChineseZodiac(new Date(2022,0,1))
all returns the same values as dotnet.
By using a calculator
2013-4=2009
2009/12= 167.41666
167*12=2004
2009-2004=5 (5 is snake which was the animal for 2013)
Use this to calculate the year.
<?php
$year = 2013;
switch (($year - 4) % 12) {
case 0: $zodiac = 'Rat'; break;
case 1: $zodiac = 'Ox'; break;
case 2: $zodiac = 'Tiger'; break;
case 3: $zodiac = 'Rabbit'; break;
case 4: $zodiac = 'Dragon'; break;
case 5: $zodiac = 'Snake'; break;
case 6: $zodiac = 'Horse'; break;
case 7: $zodiac = 'Goat'; break;
case 8: $zodiac = 'Monkey'; break;
case 9: $zodiac = 'Rooster'; break;
case 10: $zodiac = 'Dog'; break;
case 11: $zodiac = 'Pig'; break;
}
echo "{$year} is the year of the {$zodiac}.<br />";
?>
Wikipedia has a reference to 2044.
http://en.wikipedia.org/wiki/Chinese_zodiac
Using Year of the Rat as an example (for years after 1984), it looks like Rat cycles every:
383, 353, 353, 383, 354 days
Notice the last cycle is 354 which is more than likely due to Leap Year. Maybe using this formula, you can work out any year up to maybe 2100 or so.
I used the following T-SQL to deduce those numbers
select DATEDIFF(D,'02/2/1984', '02/19/1985')
select DATEDIFF(D,'02/19/1996', '02/6/1997')
select DATEDIFF(D,'02/7/2008', '01/25/2009')
select DATEDIFF(D,'01/25/2020', '02/11/2021')
select DATEDIFF(D,'02/11/2032', '01/30/2033')
If you are serious about finding a non-tabular mechanism for calculating the years of the Chinese Zodiac, then I recommend looking at 'Calendrical Calculations, 3rd Edition' which has (LISP) code to handle calculations for the Chinese New Year, and from that, deducing the Year of the <relevant-animal> is straight-forward. That book covers many calendrical systems and is an interesting read. Being a luni-solar calendar, the Chinese calendar is quite complex; the mathematics gets quite detailed.
It is probably simpler, and likely more compact, code-wise, to use a table, though.
There is always a question what is quicker to test and verify.
When developing chinese zodiac calculator on calculla, we decided to use lookup table - as this was just quicker and more convenient to code, than actually testing any algo for it (even if algo may be simple, you still need time to test it).
This lookup was not a big table and you can actually get the javascript code from source of our website.
First of all you must create an array of the zodiac signs exactly as below
DECLARE sign : [array(1.....12)]
DECLARE year : INTEGER
sign(1) ← "Rat"
sign(2) ← "Ox"
sign(3) ← "Tiger"
sign(4) ← "Rabbit"
sign(5) ← "Dragon"
sign(6) ← "Snake"
sign(7) ← "Horse"
sign(8) ← "Goat"
sign(9) ← "Monkey"
sign(10) ← "Rooster"
sign(11) ← "Dog"
sign(12) ← "Pig"
DECLARE X, Y, N : INTEGER
X ← (year - 4)
Y ← (X DIV 12) // DIV return only the natural number
Y ← (Y * 12)
N ← N + 1
OUTPUT sign(N)
Public Function ChineseSign(ByVal DoB As Date) As String
Dim CSign As String = ""
Dim YearSign As New Short
YearSign = Year(DoB) Mod 12
'// Uncomment the following to use Feb 12th as New Year calculation //
'If Month(DoB) = 1 Then
'YearSign = YearSign - 1
'If YearSign = 0 Then YearSign = 12
'ElseIf Month(DoB) = 2 And DoB.Day < 12 Then
'YearSign = YearSign - 1
'If YearSign = 0 Then YearSign = 12
'End If
Select Case YearSign
Case 1
CSign = "Rooster"
Case 2
CSign = "Dog"
Case 3
CSign = "Pig (Boar)"
Case 4
CSign = "Rat"
Case 5
CSign = "Ox"
Case 6
CSign = "Tiger"
Case 7
CSign = "Rabbit"
Case 8
CSign = "Dragon"
Case 9
CSign = "Snake"
Case 10
CSign = "Horse"
Case 11
CSign = "Goat"
Case 12
CSign = "Monkey"
End Select
Select Case CSign
Case "Ox"
Return "an " & CSign
Case Else
Return "a " & CSign
End Select
End Function