Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 250 additions & 0 deletions problem-set/FancyTribonacci.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import acm.program.ConsoleProgram;

public class FancyTribonacci extends ConsoleProgram {

/* The special symbol that breaks the input stream */
private static final int DELIMITER = -1;

public void run() {
// Receive first 3 elements of the sequence A
int A0 = readNumber("A", 0);
int A1 = readNumber("A", 1);
int A2 = readNumber("A", 2);

// For debugging purposes:
printSequence(A0, A1, A2, 20);

// Read the first element of the sequence B
int B0 = readNumber("B", 1);

// Is the sequence A made of only zeros?
if(A0 == 0 && A1 == 0 && A2 == 0) {
solveZeroSpecialCase(B0);
} else {
solveProblem(A0, A1, A2, B0);
}
}

/**
* Solves the special case when every member of A is 0
* Precondition: A0 = A1 = A2 = 0
*/
private void solveZeroSpecialCase(int currentElement) {
int currentIndex = 1;
boolean isResultPositive = true;
while(true) {
if(currentElement != 0) {
isResultPositive = false;
}
currentElement = readNumber("B", currentIndex);
if(currentElement == DELIMITER) {
break;
}
currentIndex++;
}
if(isResultPositive) {
println("Yes 0");
} else {
println("No");
}
}

/**
* Precondition: A0, A1, A2 are not all zeros
*/
private void solveProblem(int A0, int A1, int A2, int B0) {
int B1 = readNumber("B", 1);
// The sequence B only consists of one element
if(B1 == DELIMITER) {
solveProblemForOneElement(A0, A1, A2, B0);
} else {
int B2 = readNumber("B", 2);
// The sequence B is a pair
if(B2 == DELIMITER) {
solveProblemForPair(A0, A1, A2, B0, B1);
} else {
// The sequence B is at least a triplet
solveExtendedProblem(A0, A1, A2, B0, B1, B2);
}
}
}

/**
* Solves the problem when the length of the sequence B is 1
*/
private void solveProblemForOneElement(int A0, int A1, int A2, int B0) {
int firstIndexOfElement = findFirstIndexOfElement(A0, A1, A2, B0);
if(firstIndexOfElement == -1) {
println("No");
} else {
println("Yes " + firstIndexOfElement);
}
}

/**
* Solves the problem when the length of the sequence B is 2
*/
private void solveProblemForPair(int A0, int A1, int A2, int B0, int B1) {
int firstIndexOfPair = findFirstIndexOfPair(A0, A1, A2, B0, B1);

if(firstIndexOfPair == -1) {
println("No");
} else {
println("Yes " + firstIndexOfPair);
}
}

/**
* Solves the problem when the length of the sequence B >= 3
*/
private void solveExtendedProblem(int A0, int A1, int A2, int B0, int B1, int B2) {
// Now we have B0, B1, B2
int indexOfTriplet = findIndexOfTriplet(A0, A1, A2, B0, B1, B2);

boolean resultIsPositive = true;
if(indexOfTriplet == -1) {
resultIsPositive = false;
}

int nextIndex = 3;
int numberOfElements = 3;
while(true) {
int nextElement = readNumber("B", nextIndex);

if(nextElement == DELIMITER) {
break;
}

int nextElementIndex = findNth(A0, A1, A2, indexOfTriplet + numberOfElements);
if(nextElementIndex != nextElement) {
resultIsPositive = false;
}

numberOfElements++;
nextIndex++;
}

if(resultIsPositive) {
println("Yes " + indexOfTriplet);
} else {
println("No");
}
}

/**
* Reads the number from the user
*/
private int readNumber(String sequenceName, int index) {
return readInt(sequenceName + index + ": ");
}

/**
* Finds the Nth element of the given Tribonacci Sequence
* Precondition: A0, A1 and A2 should be passed as first 3 arguments
* Postcondition: Returns the Nth element of the given sequence
*/
private int findNth(int prevPrev, int prev, int current, int n) {
if(n == 0) {
return prevPrev;
}
if(n == 1) {
return prev;
}
if(n == 2) {
return current;
}
for(int currentIndex = 2; currentIndex < n; currentIndex++) {
int nextElement = prevPrev + prev + current;
prevPrev = prev;
prev = current;
current = nextElement;
}
return current;
}

/**
* Finds where the first occurence of the element is located in the given sequence
* Precondition: len(B) == 1
* Postcondition: Returns -1 if B is not a subsequence of A, otherwise returns n
* that satisfies A_n = B_0
*/
private int findFirstIndexOfElement(int A0, int A1, int A2, int element) {
int currentIndex = 0;
while(true) {
int currentElement = findNth(A0, A1, A2, currentIndex);
if(element == currentElement) {
return currentIndex;
}
if(currentElement > element) {
break;
}
currentIndex++;
}
return -1;
}

/**
* Finds where the first occurence of the pair is located in the given sequence
* Precondition: len(B) == 2
* Postcondition: Returns -1 if B is not a subsequence of A, otherwise returns n
* that satisfies A_n = B_0
*/
private int findFirstIndexOfPair(int A0, int A1, int A2, int firstElement, int secondElement) {
int currentIndex = 0;
while(true) {
int currentElement = findNth(A0, A1, A2, currentIndex);
int nextElement = findNth(A0, A1, A2, currentIndex + 1);

if(firstElement == currentElement && nextElement == secondElement) {
return currentIndex;
}

if(currentElement > secondElement) {
break;
}

currentIndex++;
}
return -1;
}

/**
* Finds where the subsequence B is located in the sequence A
* Precondition: len(B) >= 3
* Postcondition: Returns -1 if B is not a subsequence of A, otherwise returns n
* that satisfies A_n = B_0
*/
private int findIndexOfTriplet(int A0, int A1, int A2, int firstElement, int secondElement, int thirdElement) {
int currentIndex = 0;
while(true) {
int currentElement = findNth(A0, A1, A2, currentIndex);
int nextElement = findNth(A0, A1, A2, currentIndex + 1);
int nextNextElement = findNth(A0, A1, A2, currentIndex + 2);

if(firstElement == currentElement &&
nextElement == secondElement &&
nextNextElement == thirdElement)
{
return currentIndex;
}

if(currentElement > secondElement) {
break;
}

currentIndex++;
}
return -1;
}

/**
* For debugging purposes only, prints first numberOfElements members of A
*/
private void printSequence(int A0, int A1, int A2, int numberOfElements) {
for(int currentIndex = 0; currentIndex < numberOfElements; currentIndex++) {
print(findNth(A0, A1, A2, currentIndex) + " ");
}
println();
}

}
86 changes: 86 additions & 0 deletions problem-set/FancyTribonacci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# FancyTribonacci

## ამოცანა:
დაჩის ძალიან უყვარს ფაზლები და გადაწყვიტა შენი პროგრამირების ცოდნა თავისი ამოცანით შეამოწმოს. მან მოიფიქრა მიმდევრობა A შემდეგნაირად:

1. $A_0$, $A_1$ და $A_2$ მიმდევრობის წევრები ხელით ჩამოწერა
1. $n$-ური წევრი კი ასე განსაზღვრა: $A_n = A_{n-1} + A_{n-2} + A_{n-3}$, როცა $n \geq 3$

მიმდევრობის ყველა წევრი არაუარყოფითი მთელი რიცხვია.

დაჩი შენს პროგრამაში შემოიყვანს $A_0$, $A_1$ და $A_2$-ს და რაიმე $B$ მიმდევრობას, რომელიც მინიმუმ 1 წევრისგან შედგება. ანუ შეგძლიათ ჩათვალოთ, რომ მინიმუმ 4 რიცხვი შემოჰყავს შენს პროგრამაში და შემოყვანას მაშინ ასრულებს, როდესაც DELIMITER-ს ჩაწერს.

შენ უნდა დაადგინო არის თუ არა $B$ $A$-ს ქვემიმდევრობა ანუ არსებობს თუ არა რაიმე $n \geq 0$, ისეთი რომ $A_n = B_0$, $A_{n+1} = B_1$ და ასე შემდეგ.

დადებითი პასუხის შემთხვევაში უნდა დაპრინტოთ “Yes” და პირველი $n$-ის მნიშვნელობა, თუ B არ არის A-ს ქვემიმდევრობა მაშინ უნდა დაპრინტოთ “No”.

**არ გაქვთ მასივებისა და მონაცემთა სტრუქტურების გამოყენების უფლება.**

## მაგალითები:

1. $A_0 = 0, A_1 =0, A_2 = 1 \newline B_0 = 1$

პასუხი: Yes 1

1. $A_0 = 0, A_1 =0, A_2 = 1 \newline B_0 = 3$

პასუხი: No
1. $A_0 = 1, A_1 = 1, A_2 = 1 \newline B_0 = 1, B_1 = 3$

პასუხი: Yes 2
1. $A_0 = 0, A_1 = 0, A_2 = 0 \newline B_0 = 0, B_1 = 0, B_3 = 0, B_4 = 0$

პასუხი: Yes 0

## ამოცანის ამოხსნის გზა:

ამოცანის ამოსახსნელად თავიდან უნდა ავაგოთ დამხრარე ფუნქცია $findNth(A_0, A_1, A_2, n)$, რომელსაც გადაეცემა მიმდევრობის პირველი სამი წევრი და $n$ და გვიბრუნებს $A_n$-ს.

ამ ფუნქციაში ცალკე უნდა განვიხილოთ $n = 0, 1, 2$ შემთხვევები და დავაბრუნოთ შესაბამისი მნიშვნელობა. თუ $n \geq 3$, for-ციკლის დახმარებით უნდა ვიპოვოთ $A_n$. ციკლის თითოეულ იტერაციაზე უბრალოდ დავითვლით $nextElement = A_{currentIndex} + A_{currentIndex-1} + A_{currentIndex-2}$, გადავაჩოჩებთ შენახულ მნიშვნელობებს ერთით: $A_{currentIndex-2}$-ის ადგილას ჩაიწერება $A_{currentIndex-1}$, $A_{currrentIndex-1}$-ის ადგილას ჩაიწერება $A_{currentIndex}$ და $A_{currentIndex}$-ის ადგილას ჩაიწერება $nextElement$. როდესაც ციკლი დასრულდება, პასუხი იქნება $A_{currentIndex}$-ში.

ამოცანის ამოსახსნელად უნდა განვიხილოთ 4 განსხვავებული შემთხვევა:
1. $A_0 = A_1 = A_2 = 0$

თუ სრულდება პირობა: $\forall i < length(B) : B_i = 0$, მაშინ პასუხად უნდა დავპრინტოთ "Yes 0"

თუ $\exists i < length(B) : B_i \neq 0$, მაშინ პასუხია "No"

1. $length(B) = 1$

ამ შემთხვევაში უნდა ვიპოვოთ პირველი $A_n$, რომელიც მოცემული $B_0$-ის ტოლია. ამის გაკეთება შესაძლებელია მარტივი while ციკლითა და $findNth()$ მეთოდის დახმარებით. თავიდან უნდა შემოვიღოთ ცვლადი $currentIndex = 0$ და ყველა $A_{currentIndex}$-ის მნიშვნელობა შევადაროთ $B_0$-ს. თუ $A_{currentIndex} = B_0$, მაშინ დავპრინტავთ "Yes"-სა და $currentIndex$-ს, თუ $A_{currentIndex} > B_0$ მაშინ $B_0$ მიმდევრობაში არ არსებობს და დავპრინტავთ "No"-ს. ეს იქიდან ვიცით, რომ მიმდევრობის ყველა წევრი არაუარყოფითია და შესაბამისად $A_{n+1} = A_n + A_{n-1} + A_{n-2} \geq A_n$.

თუ $A = 0, 1, 0, 1, 2, 3...$ და $B = 1$
პასუხი უნდა იყოს "Yes 1"

1. $length(B) = 2$

ამ შემთხვევაში უნდა ვიპოვოთ პირველი $A_n$, რომლისთვისაც სრულდება პირობა $A_n = B_0$ და $A_{n+1} = B_1$. მსგავსად, while ციკლის დახმარებით ვიპოვი პირველ $currentIndex$-ს, რომელიც ამ ორ მოთხოვნას აკმაყოფილებს. თუ რომელიმე ეტაპზე მივიღებ $A_{currentIndex} > B_1$, ეს იმას ნიშნავს, რომ $B$ არ არის $A$-ს ქვემიმდევრობა. თუ $A_{currentIndex}$-მა $B$ მიმდევრობის ორივე წევრს გადაუსწრო, არაუარყოფითობის გამო $A$-ს არც ერთი ელემენტი $B$-ს წევრებს ვეღარ გაუტოლდება. ამ მსჯელობაში იმ დაშვებასაც ვიყენებ, რომ თუ $B$ $A$-ს ქვემიმდევრობაა, მაშინ $B_0 \leq B_1$.

თუ $A = 1, 1, 1, 3, 5, 9...$ და $B = 1, 1$
პასუხი უნდა იყოს "Yes 0"

თუ $A = 1, 1, 1, 3, 5, 9...$ და $B = 1, 3$
პასუხი უნდა იყოს "Yes 2"

1. $length(B) \geq 3$
წინა ორი შემთხვევის ცალ-ცალკე განხილვა იმიტომ მომიწია, რომ საშიშროება იყო ერთეული წევრები და წყვილები მიმდევრობაში გამეორებულიყო, მაგალითად, როგორც მოხდა $A = 1, 1, 1, 3, 5, 9...$ შემთხვევაში. მაგრამ თუ მაქვს უკვე B მიმდევრობის 3 ან მეტი წევრი მოცემული, გარანტია მაქვს, რომ A-ში იგი ქვემიმდევრობად ან ერთხელ შემხვდება ან საერთოდ არ შემხვდება.

თუ $m > n + 2$, მაშინ ვიცი, რომ $A_n \leq A_{n+1} \leq A_{n+2} \leq A_m \leq A_{m+1} \leq A_{m+2}$, თუ დავუშვებთ, რომ $A_n = A_m = B_0$, გამოგვივა, რომ $A_n = A_{n+1} = A_{n+2} = A_m = A_{m+1} = A_{m+2} = 0$, რაც უკვე განვიხილეთ.

გადავამოწმოთ ასევე თანაკვეთის შემთხვევები, რათა დავრწმუნდეთ:

1. $A_{n+2} = A_m$

$A_n = B_0, A_{n+1} = B_1, A_{n+2} = A_m = A_n = B_0 = B_2, A_{m+1} = B_1, A_{m+2} = B_2 = B_0$

$A = ... B_0, B_1, B_0, B_1, B_0 ... $

$B_1 = B_0 + B_1 + B_0 \rightarrow B_0 = B_1 = B_2 = 0$

1. $A_{n+1} = A_m$

$A_n = B_0, A_{n+1} = A_m = B_0, A_{n+2} = A_{m+1} = B_1, A_{m+2} = B_2$

$A = ... B_0, B_0, B_0, B_0 ... \rightarrow B_0 = 0$

აქედან გამომდინარე, შეგვიძლია, რომ თავიდან ვიპოვოთ $B$-ს პირველი სამი წევრის ადგილიმდებარეობა $A$ მიმდევრობაში, დავიმახსოვროთ $n$, რომლისთვისაც $A_n = B_0$ და შემდეგ $B_i$-დან დაწყებული($i \geq 3$) სათითაოდ შევადაროთ $A_{n+i}$-ს.