How to get exchange by coins with two denominations? - logic

Develop a method change(amount) that for any integer amount in the range from 24 to 1000 returns a list consisting of numbers 5 and 7 only, such that their sum is equal to amount. For example, change(28) may return [7, 7, 7, 7], while change(49) may return [7, 7, 7, 7, 7, 7, 7] or [5, 5, 5, 5, 5, 5, 5, 7, 7] or [7, 5, 5, 5, 5, 5, 5, 5, 7].
I have written something like this. But it isn't working right.
enter code here
def change (amount):
assert (amount >=24)
if amount == 24:
return [5, 5, 7, 7]
if amount == 29:
return [5, 5, 5, 7, 7]
if amount == 40:
return [5, 5, 5, 5, 5, 5, 5, 5]
coins = change(amount - 5)
coins.append(5)
return coins
for coins in range(24, 1000):
print(coins)

Actually, you should find two numbers:
the number of coins with value 7 (let it be countA) and
the number of coins with value 5 (let it be countB)
As you can see 0 <= countA <= sum/7. Then sum - countA * 7 should be divided by 5 without remainder.
So, the simple implementation in java can be like this:
import java.util.Collections;
// ...
public List<Integer> getExchange(int sum)
{
int coinA = 7;
int coinB = 5;
int maxCountOfCoinA = sum / coinA;
int countA = 0;
int countB = 0;
for (int count = 0; count <= maxCountOfCoinA; count++)
{
int remains = sum - count * coinA;
if (remains % coinB == 0)
{
countA = count;
countB = remains / coinB;
break;
}
}
List<Integer> result = new ArrayList<>();
result.addAll(Collections.nCopies(countA, coinA));
result.addAll(Collections.nCopies(countB, coinB));
return result;
}

Related

How to split range in groups?

I am writing microservice in Dart.
Service run workers by passing command args to workers like: "app.exe -a=1,2,3,4".
So if currentlyActiveWorkers is 4. Then jobs can be splited to sevaral workers like:
first: "app.exe -a=1,2,3,4"
second: "app.exe -a=5,6,7,8"
third: "app.exe -a=9,10,11,12"
fourth: "app.exe -a=13,14,15,16".
I wrote next prototype:
void main() {
int maxWorkers = 16;
int currentlyActiveWorkers = 2;
genJobs() {
int step = 1;
int sliceSize = (maxWorkers/currentlyActiveWorkers).round();
var list = [for(var i=step; i<=maxWorkers; i+=1) i];
for(int i in Iterable<int>.generate(currentlyActiveWorkers))
{
print(list.sublist(i * sliceSize, sliceSize * step));
step++;
}
}
genJobs();
}
It work fine if currentlyActiveWorkers is multiple of 2. It's generate suitable jobs numbers:
[1, 2, 3, 4, 5, 6, 7, 8]
[9, 10, 11, 12, 13, 14, 15, 16]
But there is bug if user specify for example 3. Last number 16 is loosing.
Output:
[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
[11, 12, 13, 14, 15]
It does not matter for me the number element in every group +- 1 is ok for me.
Your rounding logic is ambiguous. Besides, you should handle the last chunk of data in different way:
void main() {
print(genJobs());
}
Map<int, List<int>> genJobs() {
final activeWorkers = 3;
final maxJobs = 16;
final jobs = List<int>.generate(maxJobs, (i) => i + 1);
final workerCapacity = (jobs.length / activeWorkers).floor();
var chunks = <int, List<int>>{};
for (var workerNumber = 0; workerNumber < activeWorkers; workerNumber++) {
final startIndex = workerNumber * workerCapacity;
final endIndex = startIndex + workerCapacity;
final chunk = jobs.sublist(
startIndex,
endIndex > jobs.length || workerNumber == activeWorkers - 1
? jobs.length
: endIndex,
);
chunks.addAll({workerNumber: chunk});
}
return chunks;
}
Your problem is that you are picking a fixed size for the slices first, even when the number of elements isn't a multiple of the slice count. You're lucky that it rounded down instead of up, otherwise you'd have gotten an index-out-of-range error (try your code with 17 elements and three groups).
First you should figure our what result you want. Then you can try coding that.
For something like 22 elements and four groups, you probably want two groups of 6 elements and two groups of 5 elements, not three groups of 6 and one of 4 (since you say +/-1 is OK, not +/- 2).
I would do something like:
/// Emits the integers from 0 to [elementCount] - 1 in [groupCount] grups.
///
/// The [elementCount] must be greater than zero.
/// The [groupCount] must be in the range 1..[elementCount],
/// meaning that each group will have at least one element, and
/// each element is in at least one group.
Iterable<List<int>> evenlySpreadGroups(int elementCount, int groupCount) sync* {
if (elementCount < 1) {
throw RangeError.range(elementCount, 1, null, "elementCount");
}
RangeError.checkValueInInterval(groupCount, 1, elementCount, "groupCount");
var list = <int>[];
var groupIndex = 1;
for (var i = 0; i < elementCount; i++) {
while (i * groupCount >= groupIndex * elementCount) {
yield list;
list = [];
groupIndex += 1;
}
list.add(i);
}
yield list;
}
(It's written to also work if you allow more groups than elements, any groupCount >= 1, you'll just get empty lists in the output which is just rarely useful).
The more solutions the better:
import 'package:lists/lists.dart';
void main() {
final maxPartSize1 = 5;
final data1 = genData(1, 16);
print('data: $data1');
final parts1 = split(data1, maxPartSize1);
print('parts by $maxPartSize1: $parts1');
print('=====');
final maxPartSize2 = 3;
final data2 = genData(2, 8);
print('data: $data2');
final parts2 = split(data2, maxPartSize2);
print('parts by $maxPartSize2: $parts2');
}
List<int> genData(int start, int length) {
return List<int>.generate(length, (i) => i + start);
}
Iterable<List<int>> split(List<int> data, int step) sync* {
final length = data.length;
for (final i in StepList(0, length - 1, step)) {
var i2 = i + step;
if (i2 > length) {
i2 = length;
}
yield data.sublist(i, i2);
}
}
Output:
data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
parts by 5: ([1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16])
=====
data: [2, 3, 4, 5, 6, 7, 8, 9]
parts by 3: ([2, 3, 4], [5, 6, 7], [8, 9])
Another way:
void main() {
final maxWorkers1 = 8;
final datLen1 = 16;
final data1 = [1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16];
print('data: $data1');
final parts1 = split(data1, maxWorkers1);
print('$maxWorkers1 workers: $parts1');
print('=====');
final maxWorkers2 = 4;
final datLen2 = 11;
final data2 = genData(3, datLen2);
print('data: $data2');
final parts2 = split(data2, maxWorkers2);
print('$maxWorkers2 workers: $parts2');
print('=====');
final maxWorkers3 = 8;
final data3 = [7, 8, 3, 4];
print('data: $data3');
final parts3 = split(data3, maxWorkers3);
print('$maxWorkers3 workers: $parts3');
}
List<int> genData(int start, int length) {
return List<int>.generate(length, (i) => i + start);
}
Iterable<List<int>> split(List<int> data, int divider) sync* {
if (divider <= 0) {
throw RangeError.value(divider, 'divider', 'Must be greater than 0');
}
if (data.isEmpty) {
// Nothing to do
return;
}
final length = data.length;
divider = divider > length ? length : divider;
var partSize = length ~/ divider;
if (length != partSize * divider) {
partSize++;
}
for (var i = 0; i < divider; i++) {
final start = i * partSize;
if (start >= length) {
break;
}
var end = start + partSize;
if (end > length) {
end = length;
}
yield data.sublist(start, end);
}
}
Output:
data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
3 workers: ([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16])
=====
data: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
4 workers: ([3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13])
=====
data: [7, 8, 3, 4]
8 workers: ([7], [8], [3], [4])

What is the best approach to solve this problem?

If an array contained [1, 10, 3, 5, 2, 7] and k = 2, combine the set as {110, 35, 27}, sort the set {27, 35, 110} and split the set into array as [2, 7, 3, 5, 1, 10]
Here is a way to implement this in JavaScript:
const k = 2;
const arr = [1, 10, 3, 5, 2, 7];
// STEP 1 - Combine the set by k pair number
const setCombined = []
for(let i = 0; i < arr.length; ++i) {
if(i % k === 0) {
setCombined.push(parseInt(arr.slice(i, i + k ).join('')))
}
}
console.log('STEP1 - combined: \n', setCombined);
// STEP 2 - Sort
const sortedArray = setCombined.sort((a, b) => a - b)
console.log('STEP2 - sorted: \n', sortedArray);
// STEP 3 - Split sorted
const splitArray = sortedArray.join('').split('').map(e => parseInt(e))
console.log('STEP3 - split: \n', splitArray);
I was not sure though when you said to combine set, if you really ment to keep only unique values or not... Let me know

Insertion sort explanation

I am trying to understand the code of insertion sort, but i am really confused and i don't understand what i,j,index are doing in the code.. Can someone please help me understand more
Procedure InsertionSort(numbers : Array of Integer; size : Integer);
Var i, j, index : Integer
Begin
For i := 2 to size-1 do
Begin
index := numbers[i];
j := i;
While ((j > 1) AND (numbers[j-1] > index)) do
Begin
numbers[j] := numbers[j-1];
j := j - 1;
End;
numbers[j] := index;
End;
End.
Imo, the name index may be somewhat misleading. In this context I would have called it currentValue or something like that. That said:
Imagine an numbers -> [5, 3, 7, 2, 9, 1, 8, 4]
The algorithm start at 2 so:
i == 2; currentValue := numbers[i] (== 7)
Now the algorithm move all values located in indices (j) < i as long as j > 1 with a value < 7 one place up:
numbers[1] == 3 < 7 -> numbers[2] = 3
numbers[0] == 5 < 7 -> numbers[1] = 5
and finally write currentValue into numbers[0]. So the result after first pass would be
[7, 5, 3, 2, 9, 1, 8, 4]
Second pass (2 is smaller than any thing in the places before, so nothing happens):
[7, 5, 3, 2, 9, 1, 8, 4]
Third pass (9 is largest element in numbers[0] to numbers[4]):
[9, 7, 5, 3, 2, 1, 8, 4]
and so on ...

closest to zero [absolute value] sum of consecutive subsequence of a sequence of real values

this is an algorithmic playground for me! I've seen variations of this problem tackling maximum consecutive subsequence but this is another variation as well.
the formal def:
given A[1..n] find i and j so that abs(A[i]+A[i+1]+...+A[j]) is closest to zero among others.
I'm wondering how to get O(n log^2 n), or even O(n log n) solution.
Calculate the cumulative sum.
Sort it.
Find the sequential pair with least difference.
function leastSubsequenceSum(values) {
var n = values.length;
// Store the cumulative sum along with the index.
var sums = [];
sums[0] = { index: 0, sum: 0 };
for (var i = 1; i <= n; i++) {
sums[i] = {
index: i,
sum: sums[i-1].sum + values[i-1]
};
}
// Sort by cumulative sum
sums.sort(function (a, b) {
return a.sum == b.sum ? b.index - a.index : a.sum - b.sum;
});
// Find the sequential pair with the least difference.
var bestI = -1;
var bestDiff = null;
for (var i = 1; i <= n; i++) {
var diff = Math.abs(sums[i-1].sum - sums[i].sum);
if (bestDiff === null || diff < bestDiff) {
bestDiff = diff;
bestI = i;
}
}
// Just to make sure start < stop
var start = sums[bestI-1].index;
var stop = sums[bestI].index;
if (start > stop) {
var tmp = start;
start = stop;
stop = tmp;
}
return [start, stop-1, bestDiff];
}
Examples:
>>> leastSubsequenceSum([10, -5, 3, -4, 11, -4, 12, 20]);
[2, 3, 1]
>>> leastSubsequenceSum([5, 6, -1, -9, -2, 16, 19, 1, -4, 9]);
[0, 4, 1]
>>> leastSubsequenceSum([3, 16, 8, -10, -1, -8, -3, 10, -2, -4]);
[6, 9, 1]
In the first example, [2, 3, 1] means, sum from index 2 to 3 (inclusive), and you get an absolute sum of 1:
[10, -5, 3, -4, 11, -4, 12, 20]
^^^^^

Is it possible to calculate the Chinese Zodiac, or must I use a lookup table?

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

Resources