Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Smooth Sort #5236

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

JAPNITSINGH
Copy link

  • I have read CONTRIBUTING.md.
  • This pull request is all my own work -- I have not plagiarized it.
  • All filenames are in PascalCase.
  • All functions and variable names follow Java naming conventions.
  • All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
  • All new code is formatted with clang-format -i --style=file path/to/your/file.java

@JAPNITSINGH
Copy link
Author

Additionaly done the following to fix pervious issues:

  • I have run mvn pmd:check to ensure there are no pmd errors
  • I have run mvn checkstyle:checkstyle to ensure correct style is followed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to derive your class from SortAlgorithm?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the answer to the above questions is yes, then if should be enough to derive the SmoothSortTest from SortingAlgorithmTest.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it is possible, I just have to use generic T[] in place of Integer[] modify the code a little bit and use .compareTo() and it should be good. I shall work on it and update this PR. I'll comment here once changes are ready.

Copy link
Author

@JAPNITSINGH JAPNITSINGH Jun 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the code, haven't pushed it yet. The smooth sort algorithm I implemented seems to be failing for most arrays of size greater than 700. I guess the problem is with how I am calculating the left child of a node. I am having a look into it. I shall provide an update here once I am done with the fix

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated to code, derived the class from SortAlgorithm, removed unnecessary return statements , renamed variables to make the code more understandable.

@codecov-commenter
Copy link

codecov-commenter commented Jun 24, 2024

Codecov Report

Attention: Patch coverage is 98.21429% with 2 lines in your changes missing coverage. Please review.

Project coverage is 40.24%. Comparing base (f279f9d) to head (d5e3b95).

Files Patch % Lines
.../main/java/com/thealgorithms/sorts/SmoothSort.java 98.21% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #5236      +/-   ##
============================================
+ Coverage     39.85%   40.24%   +0.38%     
- Complexity     2437     2470      +33     
============================================
  Files           517      518       +1     
  Lines         15471    15583     +112     
  Branches       2958     2981      +23     
============================================
+ Hits           6166     6271     +105     
- Misses         9016     9018       +2     
- Partials        289      294       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@vil02 vil02 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment the code is way too complex. I would suggest to:

  • implement LeonardoHeap (or use one of the existing implementations in heaps),
  • use it in the implementation of SmoothSort.


int indexOfRightChild = rootNodeIndices.get(j) - 1; // right child is of level n-2
int indexOfLeftChild = rootNodeIndices.get(j) - 1 - getLeonardoNumbers()[currentLeonardoLevel - 2];
if (array[prevRootNodeIndex].compareTo(array[indexOfRightChild]) > 0 && array[prevRootNodeIndex].compareTo(array[indexOfLeftChild]) > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I see there is no test with array[prevRootNodeIndex].compareTo(array[indexOfRightChild]) > 0 evaluating to true and array[prevRootNodeIndex].compareTo(array[indexOfLeftChild]) > 0 to false.

To ensure that the logic is correct, please add a missing test.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I shall add a specific test case in SmoothSortTest.java.

Copy link
Author

@JAPNITSINGH JAPNITSINGH Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see why this is happening and I shall try my best to explain in this comment.

Leonardo Heap has the following three properties that define it (Going through the array from left to right):

  1. The trees are in decreasing order of levels
  2. Root nodes of the trees are in increasing order
  3. Each tree is in a 'max-heap' state.

The Level-1 leonardo Tree(L1) and Level 0 leonardo Tree (L0) are the 'base case' trees as they have exactly one node.

At any given valid leonardo heap, if the heap contains both L1 and L0 tree then the element in L0 tree will always be greater than element at L1 tree, because of the second property mentioned above ( L0 >= L1 )

When L2 tree is being constructed, the level0 tree becomes the right child level1 tree becomes the left child. And hence it is not possible to have a condition such that level0 < prevRootNodeVal < level1 as it would be a voilation of the above.

This section of the wiki page and this article (under the imageAn erroneous swap ) mention that root needs to be bigger than the new element and the roots of its child nodes for a swap to happen. However this article(in page 6) mentions that the preRootValue should exceede its largest son.

I believe the statement in wiki is more genreic statement than a technical one and this condition can be optimized to just having array[prevRootNodeIndex].compareTo(array[indexOfLeftChild]) > 0. I shall however look around for more articles that suggest otherwise before I make changes to this.

Comment on lines +16 to +17
Integer[] leonardoNumbers = {1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891, 35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457, 1664079, 2692537, 4356617, 7049155, 1405773, 18454929, 29860703, 48315633, 78176337, 126491971,
204668309, 331160281, 535828591};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not to use LeonardoNumber here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LeonardoNumber class is recursively calculating the Leonardo Numbers. I did not want to calculate(or re-calculate) the numbers everytime. I shall modify my code and use LeonardoNumber to store the results in an array before I start creating the heap and use the results stored in that array when needed.

Copy link
Author

@JAPNITSINGH JAPNITSINGH Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vil02 I however think recrsivly calculating these numbers ( before or during the execution ) might not be a great idea and would just increase the execution time as it will run the same recursion stacks multiple times. If you still suggest using LeonardoNumber.java I shall go ahead and make changes as mentioned above.

int indexOfRightChild = rootNodeIndices.get(j) - 1; // right child is of level n-2
int indexOfLeftChild = rootNodeIndices.get(j) - 1 - getLeonardoNumbers()[currentLeonardoLevel - 2];
if (array[prevRootNodeIndex].compareTo(array[indexOfRightChild]) > 0 && array[prevRootNodeIndex].compareTo(array[indexOfLeftChild]) > 0) {
swap(array, prevRootNodeIndex, currentRootNodeIndex);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
swap(array, prevRootNodeIndex, currentRootNodeIndex);
SortUtils.swap(array, prevRootNodeIndex, currentRootNodeIndex);

}
} else {
// swap
swap(array, prevRootNodeIndex, currentRootNodeIndex);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
swap(array, prevRootNodeIndex, currentRootNodeIndex);
SortUtils.swap(array, prevRootNodeIndex, currentRootNodeIndex);

Comment on lines 179 to 184

private static <T> void swap(T[] array, int idx, int idy) {
T swap = array[idx];
array[idx] = array[idy];
array[idy] = swap;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private static <T> void swap(T[] array, int idx, int idy) {
T swap = array[idx];
array[idx] = array[idy];
array[idy] = swap;
}

}

if (array[childIndexForSwap].compareTo(array[currentRootNodeIndex]) > 0) {
// swap(And keep on swapping I guess, I did not implement that which might be causing issue?)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was added during debugging, I shall remove this in the next commit.

Comment on lines 165 to 166
// swap
swap(array, prevRootNodeIndex, currentRootNodeIndex);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// swap
swap(array, prevRootNodeIndex, currentRootNodeIndex);
SortUtils.swap(array, prevRootNodeIndex, currentRootNodeIndex);

No need for the comment. The name of the method swap is pretty clear.

leonardoTreeLevelforHeapify = currentLeonardoTreeLevels[j - 1];
}
j = j - 1;
if (j == i - 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

j == i - 1 is never false (among the existing test cases).

Copy link
Author

@JAPNITSINGH JAPNITSINGH Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do have a test case (not currently committed) with around 900 entries where this evalueates to false during creation of the heap. I can try to reduce the entires in such a way that the condition still evaluates to true, I will for sure end up having an array of size 20-30 ( as i will need 3 trees of non consecutive levels .... L1 L3 and L5 at least ).

Would it be fine if I added a test case in SmoothSortTest.java with around 20-30 elements in the test? If yes, I shall work on it at the very end after resolving the other conversations.

Copy link
Author

@JAPNITSINGH JAPNITSINGH Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: Yes, this condition is never false. I belive what I intented to do was run maxHeapifyLeonardoTree if you reached the left most tree and further swaps are still present.

I am checking this as well.

Comment on lines 201 to 202
// maxHeapifyLeonardoTree(rightChildIndex, currentLeonardoLevel - 2, array);
// maxHeapifyLeonardoTree(leftChildIndex, currentLeonardoLevel - 1, array);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This too was added for debugging, I shall remove this in my next commit. please ignore this.

Comment on lines +35 to +44
if (consecutiveTreeIndices[0] != -1) {
// if 0th or 1st index is -1 that implies there are no concequtive trees
leonardoLevelTracker = leonardoLevelTracker & ~(1 << consecutiveTreeIndices[0]);
leonardoLevelTracker = leonardoLevelTracker & ~(1 << consecutiveTreeIndices[1]);
leonardoLevelTracker = leonardoLevelTracker | (1 << consecutiveTreeIndices[1] + 1);
} else if ((leonardoLevelTracker & 2) == 0) {
leonardoLevelTracker = leonardoLevelTracker | (1 << 1);
} else {
leonardoLevelTracker = leonardoLevelTracker | (1 << 0);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to apply the SingleBitOperations (or something from bitmanipulation)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not come accross this class earlier.
Yes, this class can definately be used. I shall modify this section of the code as well.

@JAPNITSINGH
Copy link
Author

JAPNITSINGH commented Jun 25, 2024

At the moment the code is way too complex. I would suggest to:

  • implement LeonardoHeap (or use one of the existing implementations in heaps),
  • use it in the implementation of SmoothSort.

I did try to use existing implementaions of Heaps, however Leonardo Heap is a bit different from the others since this heap contains multiple loenardo trees of very specific shapes and the root node being the right most elemnent in the list.

I can crate a new class LeonardoHeap in heaps, however I believe I might not endup using the interface Heap.java in this implementation, would that be alright?

@JAPNITSINGH
Copy link
Author

Adding this comment to provide updates and ensure this PR is not marked as stale:

I have refactored the code, not yet pushed as I want to do some more changes and try to use the interface Heap.java
I shall comment here once the changes are ready to be reviewed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants