However, in some cases, you might realize that the changes that you made are ** not that good** or the

Don't worry much because, Git has got your back!

The preferred method of undoing shared history is **git revert**.
A **revert** is safer than a **reset** because it will not remove any commits from a shared history. ** A revert will retain the commits you want to undo and create a new commit that inverts the undesired commit**. This method is safer for shared remote collaboration because a remote developer can then pull the branch and receive the new revert commit which undoes the undesired commit.

Instead use `git revert`

```
git log
#This will show the previous commits and their hashes. Copy 6 char hash of initial commit.
git revert <hash of initial commit>
```

You can check by running

```
git status
git log
```

It is a versatile tool for developers to use.
`git reset`

is used for undoing changes on a private branch/locally. It has three modes depending upon where you want to reflect changes in Git's internal state management mechanism's. Git has three such state managements: The Commit Tree (HEAD), The Staging Index, and The Working Directory.

This is the safest reset mode.
--soft resets the head to , just like all modes do.
It **does not touch the index file or the working tree at all** and leaves all your changed files as "Changes to be committed", as git status would put it.
The Staging Index and the Working Directory are left untouched so changes are not lost in either places. Only Commit Tree is altered.

```
git log
#This will show the previous commits and their hashes. Copy 6 char hash of initial commit.
git reset --soft <hash of initial commit>
```

You can check by running

```
git status
git log
```

This is the default operating mode. --mixed has the following impacts:

- The ref pointers are updated.
- The Staging Index is reset to the state of the specified commit.
**Any changes that have been undone from the Staging Index are moved to the Working Directory**(you can recover by`git add file-1`

) ``` git log #This will show the previous commits and their hashes. Copy 6 char hash of initial commit.

git reset

```
You can check by running
```

git status git log

```
## --hard
This is the most DANGEROUS option. Use only when you're sure that you want to delete your work.
--hard has the following impacts
1. The Commit History ref pointers are updated to the specified commit.
2. The Staging Index and Working Directory are reset to match that of the specified commit.
**Any changes made are lost and matches to the state of the specified commit**
```

git log

#This will show the previous commits and their hashes. Copy 6 char hash of initial commit.

git reset --hard

```
You can check by running
```

git status git log ``` Here's summing up the three modes, where they render changes.

[This article is contributed by our Senior Frontend Developer Miss Kulsoom , Thank you Miss Developer π€]

]]>This is why branches are a massive feature. Saving you right?

##Working on a branch Here's how you can work on branches:

1) Make a new branch
`git checkout -b <branch name>`

If you want to switch to an existing branch use
`git checkout <branch name>`

2) Open the editor of your choice and make changes

3) Add the files
`git add .`

4) Commit the changes and add a commit message
`git commit -m "commit message"`

5) Finally, push these changes to the origin
`git push origin`

If error occurs, it's probably because you've not set your remote properly . Try using
`git remote set-url origin SSH of your repo`

OR
`git push --set-upstream origin <branch name>`

Congrats! You've merged your branch into the master!

##Deleting a branch

`git branch -d <branch name>`

Also delete branch from origin using

`git push origin --delete <branch name>`

[This article is contributed by our Senior Frontend Developer Miss Kulsoom , Thank you Miss Developer π€]

]]>Given n items having certain value and weight, put these items in a knapsack with given maximum capacity (maxWeight). The goal here is to find possible maximum total value in the knapsack. Here fraction of items can be used i.e we can break an item. For example :

Input: n=3 maxWeight=50 weights={10,20,30} value={50,40,90}

Output: 160.00

In the above example, we can fully select item 0 and item 2. This will give total value = 50+90 = 140 with total weight = 10+30 = 40. For remaining weight space of 50-40=10, we can use (1/2)th of item 1 which will have weight = 20/2 = 10 and value 40/2=20. So total value will be 50+90+20 = 160 and total weight will be 10+30+10=50 i.e. <=maxWeight. This is the possible maximum answer.

Here the key observation is that we need to use items fully until we lack weight space for the particular item. In that scenario, we can break the item to fill the left weight space.

As we can break the item into any fraction and use it, we need to normalize it. Suppose we have an item of weight = a and value = b, we can break that item into 'a' number of items each of value (b/a). For eg. if we have an item of weight = 30 and value = 120, we can break that item into 30 items, each having value (120/30)=4 i.e. we have 30 items having weight = 1 and value = 4.

To maximize the total value inside the knapsack, we will sort the items with reference to (value/weight) ratio using the custom compare function in Sort(). One by one we will check for an item and as per the left weight space out of maxWeight inside the knapsack, we will use the current weight.

Case 1: If our current item is having a weight less than or equal to the left weight space inside the knapsack, we didn't need to break the item as the whole item can be accommodated inside, so we will add that item fully. If we have an item of weight 10 and the left weight space in the knapsack is 20 then we can use that item fully without breaking it.

Case 2: If our current item is having a weight more than the left weight space inside the knapsack, we will break the item and use only the fraction that is required. For eg., if we have the capacity(max weight) of knapsack = 10 and the current total weight of knapsack = 8. Current item is having weight = 30 and value = 120. In order to fill 10-8 = 2 weight space in knapsack we will use only (1/15)th of item i.e weight = (30/15) = 2 having value (120/30)*2 = 8.

```
struct Item{
int weight,value;
};
class greedy_approach{
static bool compare(Item a,Item b){
return (double)a.value/a.weight>(double)b.value/b.weight;
}
public:
double fractional_knapsack(int maxWeight,int n,Item weights[]){
sort(weights,weights+n,compare);
double ans = 0 ;
for(int i=0;i<n;i++){
if(maxWeight>=weights[i].weight){
maxWeight-=weights[i].weight;
ans+=weights[i].value;
}else{
ans+=maxWeight*(double)weights[i].value/weights[i].weight;
break;
}
}
return ans;
}
};
```

Here we use Item struct to reduce code complexity and can avoid making extra vectors and maps to store weights and values.

**Time Complexity** : O(n*logn). We use Sort() and an O(n) loop. The sort takes approx. n*logn time.
**Space Complexity** : O(n).

[This article is contributed by Mr Pradeep , thank you Mr Pradeep π€]

]]>**Why Data Science?**
->Traditionally, the data that we had was mostly structured and small in size, which could be analyzed by using simple BI tools. Unlike data in the traditional systems which was mostly structured, today most of the data is unstructured or semi-structured.

Lets have a look at the data trends in the image given below:

This data is generated from different sources like financial logs, text files, multimedia forms, sensors, and instruments. Simple BI tools are not capable of processing this huge volume and variety of data. This is why we need more complex and advanced analytical tools and algorithms for processing, analyzing and drawing meaningful insights out of it.

Lets have a look at the below to see all the domains where Data Science is creating its notion.

**Lifecycle of Data Science**
Here is an overview of the main phases of the Data Science Lifecycle:

**Different job roles data science offers are:**

- Data Scientist
- Data Analyst
- Data Engineer
- Business Intelligence Developer and many more.

**Data Science Skills**

[This article has been contributed by Miss Tanu,Thank you Miss Tanu]

]]>###A Clean Commit Tree
* Squashing your commits* can help so here we go

Switch to the branch where the commit tree is

`git checkout <branch name>`

Make sure everything on your local is pushed to the branch

`git add .`

`git commit -m "Commit message"`

`git push origin <branch name>`

Squash n previous commits

*(n is the no.of commits you want to squash)*`git rebase -i HEAD~3`

Now, change * pick* to

`Ctrl + O`

to write out
b) `Enter`

to save changes
c) `Ctrl + X`

to exitNow you can edit the final commit message . Repeat a,b,c

Don't forget to `PUSH`

your changes on to the branch
`git push origin <branch name>`

If this doesn't work force push your changes
`git push origin <branch name> -f`

You're Done! Now you have a clean commit tree.

[This article is contributed by our Senior Frontend Developer Miss Kulsoom, Thank you Miss Developer π€]

]]>##Problem Description Given n weights having a certain value put these weights in a knapsack with a given capacity (maxWeight). The total weight of the knapsack after adding weights must remain smaller than or equal to capacity(maxWeight). The goal here is to find possible maximum total value in the knapsack. Any weight can be selected multiple times i.e. repetitions are allowed. For example :

Input: n=2 maxWeight=3 weights={2,1} value={1,1}

Output: 3

In the above example, we can select weights {1} for 3 times giving knapsack total weight as 3 and total value as 3 which is the maximum possible value.

##Observation Here the key observation is that we need to find the subset of weights whose weight sum is less than or equal to the capacity of the knapsack and having maximum value sum. Here any weight can be added multiple times in the subset.

#Recursive Approach Recursively we will move from index 0 to n-1 and will perform two operations:

Add the current weight. We will only select the current weight if the total weight inside the knapsack after selecting remains under the given capacity else will ignore it. If selected, we will call for the same index again because we can select a weight more than once.

Don't add the current weight. Here we will move to the next index (i.e i+1)

We will return the maximum of both operations as we have to maximize the total value. Here we will stop at index==n because there's no weight to add in the knapsack. You can see in the code that it is selecting the weight and calling the same index again to check if the same weight can be added again. Also, we are considering the possibility of not selecting the weight. So in the end, the maximum answer for a particular weight after considering both possibilities is returned. At last, we have final answer.

```
int rec_helper(int curWeightSum,int i,int n,int wt[],int val[],int maxWeight,int **dp){
if(i==n){
return 0;
}
if(dp[i][curWeightSum]!=-1){
return dp[i][curWeightSum];
}
dp[i][curWeightSum] = rec_helper(curWeightSum,i+1,n,wt,val,maxWeight,dp);
if(curWeightSum+wt[i]<=maxWeight) {
dp[i][curWeightSum] = max(dp[i][curWeightSum],
val[i] + rec_helper(curWeightSum + wt[i], i, n, wt, val, maxWeight, dp));
}
return dp[i][curWeightSum];
}
int unbounded_knapsack(int n,int wt[],int val[],int maxWeight){
int** dp=new int*[n];
for(int i=0;i<n;i++){
dp[i]=new int[maxWeight+1];
for(int j=0;j<maxWeight+1;j++){
dp[i][j]=-1;
}
}
return rec_helper(0,0,n,wt,val,maxWeight,dp);
}
```

Memoization - Here I used a 2d array - 'dp' to store the recursion calls so that we can use the stored value in dp for repeated recursion calls instead. This is useful for reducing time complexity.

**Time Complexity** : O(n*maxWeight*maxWeight). For every weight we are going till maxWeight. And every weight is selected for multiple times reaching maxweight.
**Space Complexity** : O(n*maxWeight). We used 2-d array dp as extra space

#Dynamic Approach Tabulation is done using a one-dimensional array(dp) in this case. Whose dimension is the same as maxWeight+1. Here we do the same two operations stated in the recursive approach:

If we have to form a knapsack of capacity j and we have current weight under consideration as weights[i], which is <=j, with value[i]. If we add the current weights[i] in the knapsack, we need to find the maximum value for the knapsack of total weight (j-weight[i]) from the dp which contains solutions till current weight(included), and then add value[i] of weights[i] weight in that. In case we don't have a knapsack of total weight (j-weights[i]) then 0 will be added and the value[i] of current weights[i] will become the total of values for that particular knapsack. If we have the current weight as 3 and required j=5 then we need to find a knapsack of weight 5-3=2. Here the important difference from 0-1 knapsack problem is that we are iterating the dp from 0 to maxWeight(included). This is because we also need solutions for current weight till the current required knapsack weight(j) as weights can be used for multiple times.

If we don't select the weight, the value of that cell remains unchanged as it already describes the answer till now for particular j.

```
int unbounded_knapsack(int n,int wt[],int val[],int maxWeight){
int dp[maxWeight+1];
for(int i=0;i<maxWeight+1;i++){
dp[i]=0;
}
for(int i=0;i<n;i++){
for(int j=wt[i];j<maxWeight+1;j++){
dp[j]=max(dp[j],val[i]+dp[j-wt[i]]);
}
}
return dp[maxWeight];
}
```

Here the cell value in the dp array will be the maximum of operation1 and operation2. I used 1-d array, where jth cell represents a knapsack with weight j while performing operations on a particular ith weight. Hence the array will hold results till (i-1)th weight while running for ith weight. Returning dp[maxWeight] will give the answer. Initially, all the values of the array is 0 as it will be the maximum for an empty knapsack.

**Time Complexity** : O(n*maxWeight).
*Space Complexity* : O(maxWeight). We used 2-d array dp as extra space

[This article is contributed by Mr Pradeep, thank you Mr Pradeep π€]

]]>We came to know that Computers can have the vision to watch/sense and learn from humans, then try to mimic the action with utmost accuracy. So we have three queries now,

```
a) How computer watch/sense
b) How computer learn
c) How computer mimic
```

The first question can be answered by the Computer Vision and Robotics, we use different sensors and hardware along with support precision making programs/software to correctly input data to the computer/system.

The second question starts with two things available as a parameter - The first one is the data that we have collected (From the solution of the first question) and the second one is patterns and inference*.

Now coming to the definition,

Machine Learning is the study of algorithms and mathematical/statistical models, to find out some pattern and perform a certain task without being explicitly instructed

I have underlined four important terms, I am not going to explain them step by step, but I will explain what exactly happens and the mechanism we generally follow. (Please search for the terminology if you are unable to understand)

So we have a set of data, and we choose a random sample(most preferable Stratified Random Sample) of data as Training Data, now comes the actual work of designing an algorithm and feeding it to the system as some sort of Code. Our training data is the input of the algorithm and we get a mathematical/statistical model as output(say P). Over this core layer of data, Algorithm and output, we have a second layer of decision making and task performing programs designed for a specific work, that takes P as input and performs the task. Then comes the answer for the third question, how computers mimic, Robotics and Expert System

Well, arguably, an expert system is a cover layer for Machine Learning and Deep Learning, because that includes programs to choose between better ML Algorithm to perform the task, briefly, it emulates the decision-making ability of human expert.

So basically, we have the following outcome till this part of the text,

Before explaining that, we have a new terminology - Neural Networks. Most of you might know about what a neuron is, well thats a group of cells responsible for the transfer, modification and response of information inside the human body from the human brain to another part of the organs. In a similar manner, neural networks are programs designed in such a way that the system can learn and further improve its performance like a human body does. As this is the first assignment, I am not discussing the technicalities involved in details.

Deep Learning is a subset of machine learning, focused on developing the ML Algorithm into layers of data and extract a broader sense of details(technically known as features and attributes) for a more precise and accurate prediction or learning depending upon the target attribute/feature

Forget the definition, lets make that simpler, We are giving the system this input as an image. Let's see what ML and DL algorithms will predict.

**ML Algorithm** - 4 leg, 1 tail, 2 horns, 2 ears, size - X
**DL Algorithm** - 4 leg, 1 tail, 2 horns, 2 ears, size - X, head size: body size = P:Q, 20% black spot, 80% white , cluster of black spot near the neck is more, legs have less amount of black spot

You must be thinking, how does that even matter? ML Algorithm output is sufficient to identify its a cow, isnt it? Let's have two images to compare,

Your ML Algorithm may fail to distinguish and categorize these two as different(provided the size is the same) but the DL will definitely predict the correct answer because each of the pictures has cows with different ratio of black spot and white spot in their body.

[ Arguably and Technically this is a wrong example, it is meant to just give an idea that DL is nothing but just an extension and enhancement of ML]

]]>- Recursion
- Top Down DP
- Bottom Up DP
- Space Optimised Approach.

We will be discussing each approach in details.

So, Let's Start!

**1. Recursion**

We know that in Recursion, a function calls itself multiple times till it hits the base case to solve a particular problem. If we initially try to understand how exactly is recursion working then it will be a problem and we may get confused and may not find a way to solve the problem. Initially we should be focusing on large problem and how it can be solved. Then we should define a function and tell it where to stop i.e specify the base case and write code to solve the larger problem and leave the rest in hands of recursion. Like for the Fibonacci series we know that 1st term is 1 and nth term is sum of previous 2 terms i.e n-1 and n-2. So lets write that and leave the rest to recursion.

```
int fibonacci(int n)
{
if(n==1 || n==0)
return n;
return fibonacci(n-1)+fibonacci(n-2);
}
```

The above function is going to work absolutely fine but **is this an an optimal approach**?

Lets try to understand it. Suppose using above function we are trying to find the 5th Fibonacci term. The above code will generate the following recursion tree.

We can see that recursion tree repeats for some elements a number of times (Highlighted in **Red**) i.e same work is done again and again. The number of repetition will be even more higher for large numbers. Also the above code has time complexity of O(2^n) which is exponential and is really very bad.

So the answer to above question is **NO**. This is not an optimal approach.

Nothing to worry about π. Dynamic Programming will sort it out.

So what's Dynamic Programming then?

A quote by **Winston Churchill** says:

**Those who fail to learn from history are condemned to repeat it.**

This is what Dynamic Programming does. We store the already calculated value so that it can be used as and when required and re-calculation can be avoided.

Let's Discuss them!

**2. Top Down DP**

When we start from large sub-problem and reach base case it is called Top Down DP Approach. In this approach we use **Recursion + Memoization** i.e calculate the value using recursion and store it in some array so that we don't have to re-calculate.
Lets see how can Fibonacci Series problem can be solved with this approach.

```
int dp[100]={0}; // I am assuming size to be 100. It can be anything
int fibonacci(int n)
{
if(n==0||n==1)
return n;
if(dp[n]!=0)
return dp[n]; //If the value has already been calculated, return it
dp[n] = fibonacci(n-1)+fibonacci(n-2);
return dp[n];
}
```

What do you say about the above code ? Far better then that of recursion (of course not in length :)).
So, **the time complexity of above code is O(N) and Space Complexity of O(N) is also required**.

**3. Bottom Up DP**

In this approach we start from base case and reach the required solution. No recursion is needed in this approach.

Have a look at the code.

```
int fibonacci(int n)
{
int dp[100]={0}; // I am assuming size to be 100. It can be anything
dp[1]=1; //We need to provide starting values
for(int i=2;i<=n;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
```

**Time and Space Complexity remains the same as above i.e O(N).**

I hope that the above explanation and code gave you some idea and flavour of what Dynamic Programming is and how it works. Don't worry. I have not forgotten the 4th section. Its **not** something very much related to DP so I will discuss it below π.

So from the above discussion we can conclude that DP highly optimised the code and saves computational resources like memory. A huge amount of practice is required for achieving expertization in Dynamic Programming and having knowledge of other algorithms may also help in reaching an optimal DP Solution.

Now its time for Space Optimised approach.

**4. Space Optimised Approach**
This approach may not necessarily work for all problems as it works for Fibonacci Sequence. In this approach we can find the nth element even without using any array!

Have a look.

```
int fibonacci(int n)
{
int a=0,b=1,c;
for(int i=2;i<=n;i++)
{
c=a+b;
a=b;
b=c;
}
return c;
}
```

So, lets end our blog here. Thanks to all the readers for their time. Any feedbacks are always welcome. Hope to see all of you in my upcoming blogs as well. Thank You π.

[This post has been contributed by our blogger Mr Sourav , Thanks Mr Sourav π€]

]]>The easiest approach to do this, if one knows how to use "for" loops or implement it (if it is not implemented already) in any given programming language is just a single O(n) iteration. Below is a sample implementation in C++.

```
int a, n;
// a, n can be initialized either by taking input or
// by assigning hardcoded values a = 3, n = 4 let's say
// expo initialized to 1 as it is the multiplicative identity
int expo = 1;
for (int i = 0; i < n; ++i) {
expo = expo * a;
}
// now expo == a^n : true
```

**Note**
Here, the data type is "int", assuming the value of expo = a^n comes under the constraint of "int". So, any other data type can be used as per requirements.

**Caveats**

- It can be seen that this approach would time out if n > 10^8.
- If the value of a^n is very large i.e. it can not be stored in "int" or any other provided data type, it will overflow.

**Possible Solutions**

- Algorithm of lesser complexity can be utilized in this case -- we will see O(log n) algorithm next.
- This is the reason why most of the time (a^n)%(some number) is to be calculated. Most of the time that "some number" is 1e9 + 7 (which is a prime) in competitive programming problems.

For achieving O(log n) complexity, the mathematical fact that any number (in decimal) can be represented uniquely in binary can be utilized.

For example,
12 = 1*8 + 1*4 + 0*2 + 0*1 = (1100)_2
15 = 1*8 + 1*4 + 1*2 + 1*1 = (1111)_2

Now, also using the fact that a^(m + n) = (a^m)*(a^n), we can calculate the values of a^1, a^2, a^4, and so on ...

```
int expo = a;
for (int i = 0; i < n; ++i) {
expo = (expo*expo);
}
// it's like
// expo: a -> a^2 -> a^4 -> a^8 -> a^16 ...
// i.e. with ith step the value will be a^(2*i)
```

Now, let's say we need to calculate a^n, then there will exist a unique representation of "n" in binary, whose i_th bit can be checked if it is set or not by using a simple boolean expression involving bitwise operator

```
// (n >> i) & 1 == ith bit of n
```

for the proof for this refer to Link

now, it can be seen that a^n = (a^(1*b_1))x(a^(2*b_2))x(a^(4*b_3))x... and we can find if the a^(2*i) have to multiply or not by using the fact we saw above. So, by a simple modification to the previous code we can calculate a^n.

```
//init a, n
int expo = a;
// answer initialized to 1 as it is multiplicative identity
int answer = 1;
for (int i = 0; i < NUMBER_OF_BITS_IN_N; ++i) {
if((n >> i) & 1) {
// we check if the ith bit is set or not
// if it is set then, we multiply expo = a^(2*i)
answer = answer*expo;
}
expo = (expo*expo);
}
// answer now have the value a^n
```

See, there is only one "O(NUMBER_OF_BITS_IN_N)" for loop, and it is easy to see that the number of bits in n = log_2(n).

Hence, the overall complexity = 0(log n)

If you are not sure of the number of bits in n, then just simply take MAXIMUM_POSSIBLE_NUMBER_OF_BITS instead which can be ~32 for the int datatype.

Considering the second caveat described above, there can be cases where we need to find (a^n)%(some value) -- note that % is the remainder operator (as used in C++).

For this, just an easy modification of the code will work,

```
//init a, n, modvalue
long long expo = a;
// answer initialized to 1 as it is multiplicative identity
long long answer = 1;
for (int i = 0; i < NUMBER_OF_BITS_IN_N; ++i) {
if((n >> i) & 1) {
// we check if the ith bit is set or not
// if it is set then, we multiply expo = a^(2*i)
answer = (answer*expo) % modvalue;
}
expo = (expo*expo) % modvalue;
}
// answer now have the value (a^n)% modevalue
```

So, as we saw the binary exponentiation algorithm, it can be used for exponentiating a matrix too, just by changing "*" operator with implemented matrix multiplication and taking remainder with each number in the matrix.

For reading more about binary exponentiation and solving some related problems to get a grasp refer to CP-Algorithms Binary-Exp

[This article is contributed by our blogger Mr Rishabh, Thanks Mr Rishabh π€]

]]>Given n weights having a certain value put these weights in a knapsack with a given capacity (maxWeight). The total weight of the knapsack after adding weights must remain smaller than or equal to capacity(maxWeight). The goal here is to find possible maximum total value in the knapsack. Any weight can be selected only once. For example :

Input: n=4 maxWeight=5 weights={4,3,5,1} value={1,2,2,3}

Output: 5

In the above example, weights:{3,1} with value:{2,3} respectively will have a total value of 5 which is the maximum among any combination of weights.

Here the key observation is that we need to find the subset of weights whose weight sum is less than or equal to the capacity of the knapsack and having maximum value sum.

Recursively we will move from index 0 to n-1 and will perform two operations:

Add the current weight (Add element to a subset). We will only select the current weight if the total weight inside the knapsack after selecting remains under the given capacity else will ignore it.

Don't Add the current weight (Don't add an element to a subset).

We will recursively call the next element after performing may be any one or both operations given above as per the conditions. We will return the maximum of both operations as we have to find the maximum total value. We have to stop and return when we reach beyond the array size. So in the end, we will have a maximum of all such subsets where we made choices to select the weight with a certain value or ignore it.

```
int rec_helper(int curWeightSum,int i,int n,int wt[],int val[],int maxWeight,int **dp){
if(i==n){
return 0;
}
if(dp[i][curWeightSum]!=-1){
return dp[i][curWeightSum];
}
dp[i][curWeightSum] = rec_helper(curWeightSum,i+1,n,wt,val,maxWeight,dp);
if(curWeightSum+wt[i]<=maxWeight) {
dp[i][curWeightSum] = max(dp[i][curWeightSum],
val[i] + rec_helper(curWeightSum + wt[i], i + 1, n, wt, val, maxWeight, dp));
}
return dp[i][curWeightSum];
}
int knapsack(int n,int wt[],int val[],int maxWeight){
int** dp=new int*[n];
for(int i=0;i<n;i++){
dp[i]=new int[maxWeight+1];
for(int j=0;j<maxWeight+1;j++){
dp[i][j]=-1;
}
}
return rec_helper(0,0,n,wt,val,maxWeight,dp);
}
```

Memoization - Here I used a 2d array - 'dp' to store the recursion calls so that we can use the stored value in dp for repeated recursion calls instead. This is useful for reducing time complexity.

**Time Complexity** : O(n*maxWeight). As we avoided most repeating calls.
Space Complexity : O(n*maxWeight). We used 2-d array dp as extra space

Tabulation is done using a one-dimensional array in this case. Whose dimension is the same as maxWeight+1(capacity of knapsack). Here we do the same two operations stated in the recursive approach:

If we have to form a knapsack of capacity j and we have current weight under consideration as weights[i], which is <=j, with value[i]. If we add the current weights[i] in the knapsack, we need to find the maximum value for the knapsack of total weight (j-weight[i]) till previous iterations and then add value[i] of weights[i] weight in that. In case we don't have a knapsack of total weight (j-weights[i]) then 0 will be added and the value[i] of current weights[i] will become the total of values for that particular knapsack. If we have the current weight as 3 and required j=5 then we need to find a knapsack of weight 5-3=2.

If we don't select the weight, the value of that cell in dp array will be the answer to the previous iteration for that particular j.

```
int dp[maxWeight+1];
for(int i=0;i<maxWeight+1;i++){
dp[i]=0;
}
for(int i=0;i<n;i++){
for(int j=maxWeight;j>=wt[i];j--){
dp[j]=max(dp[j],val[i]+dp[j-wt[i]]);
}
}
return dp[maxWeight];
```

Here the cell value in the dp array will be the maximum of operation1 and operation2. I used 1-d array, where jth cell represents a knapsack with weight j while performing operations on a particular ith weight. Hence the array will hold results till (i-1)th weight while running for ith weight. Returning dp[maxWeight] will give the answer. Initially, all the values of the array is 0 as it will be the maximum for an empty knapsack.

**Time Complexity** : O(n*maxWeight).
*Space Complexity* : O(maxWeight). We used 2-d array dp as extra space

[This article is contributed by Mr Pradeep , thank you Mr Pradeep π€]

]]>In computer science many a time we come across optimization problems, where we have to optimize a certain variable in accordance with some other variables. Optimization means finding a maximum or minimum. For example,

- Finding the shortest path between two vertices in a graph.
- Finding the minimum number of colours needed to colour a graph, etc.

These are a little bit different from the Optimization problems. In this type of problems, the main target is to find whether a solution exists or not, just like a typical yes or no question. For example,

- Checking if a graph is Hamiltonian
Checking whether a cycle exists in a Linked List, etc. Programming and mathematical problems that we have observed in daily life can be categorized into two classes problems,

P class

- NP class

The class P consists of those problems that are solvable in polynomial time by the turing machine, i.e. these problems can be solved in time **O(n^k)** in worst-case, where **k** is constant.These problems are also known as tractable.

An algorithm **A** is a polynomial time algorithm, if there exists a polynomial **p(n)** such that the algorithm can solve any instance of size n in a time **O(p(n))**.
Algorithms such as Matrix Chain Multiplication, Single Source Shortest Path, All Pair Shortest Path, Minimum Spanning Tree, etc. run in polynomial time.

NP is the class of decision problems for which it is easy to check the correctness of a claimed answer, with the aid of a little extra information. NP consists of those problems that are verifiable in polynomial time. Hence, we arent asking for a way to find a solution, but only to verify that an alleged solution really is correct.

Every problem in this class can be solved in exponential time using exhaustive search. Problems such as travelling salesperson, optimal graph colouring, Hamiltonian cycles etc for which no polynomial-time algorithms is known, are classified as NP-complete problems.

The formal definition says,

Given an optimization problem P, an algorithm A is said to be an approximate algorithm for P, if for any given instance I, it returns an approximate solution i.e. a feasible solution.

An Approximation Algorithm is a way of approach NP-completeness for any optimization problem. This does not guarantee the best solution. The main purpose of an approximation algorithm is to come as close as possible to the optimum value at the most polynomial time. Such algorithms are called approximation algorithms.

For example -For the travelling salesperson problem, the optimization problem is to find the shortest cycle, and the approximation problem is to find a short cycle.

Let's discuss some famous Approximation Algorithms with their problem statements,

**Problem Statement**
Given an undirected graph, the vertex cover problem is to find a minimum size vertex cover.
As the name suggests, A vertex cover of an undirected graph is a subset of its vertices such that for every edge (x, y) of the graph, either x or y is in the vertex cover.
This problem is an NP-complete problem and can be solved approximately i.e. no polynomial time solution(unless P = NP).

```
1) Initialize the result as an empty list {}
2) Consider a set of all edges in a given graph. Let the set be E.
3) while E is not empty
a) Pick an arbitrary edge (x, y) from set E and add x and y to result
b) Remove all edges from E which are either incident on x or y.
4) Return result
```

The time complexity of the above algorithm is O(V+E) where V is the number of vertices in the graph and E is the number of edges.

**Problem Statement**
Given a set of cities and the distance between every pair of cities, the problem is to find the shortest possible route that visits every city exactly once and returns to the starting point.

This problem can be converted into a graph problem, which has V nodes(i.e. number of cities) and the cost matrix will hold the edge weights(i.e. the distance between any two cities). The problem is a famous NP-hard problem. There is no polynomial-time to know a solution to this problem.

A naive approach to solve the problem can be,

```
1) Consider city 1 as the starting and ending point.
2) Generate all (V-1)! Permutations of cities.
3) Calculate cost of every permutation and keep track of minimum cost permutation.
4) Return the permutation with minimum cost.
```

The above algorithm works in O(V!) time complexity, where V is the number of vertices in the graph.

There are other approaches using Bitmasking and Dynamic Programming to solve in O(V^2 * 2^V) time complexity. Branch and Bound can be one approach in finding a solution for this NP-Hard problem.

This problem can be easier to approximate if the edge weights of the graph follow a triangle inequality, which means `w(u,v) <= w(u,x) + w(x,v)`

, i.e. distance of city u to city v is less than the sum of the distance of city u to city x and city x to city v. This problem is also known as Metric TSP, which is an approximate version of the problem. This is still NP-Hard, but we can approximate the solution.

**Problem Statement**
Given n items of different weights and bins each of capacity c, assign each item to a bin such that number of total used bins is minimized. It may be assumed that all items have weights smaller than bin capacity.
In computational mathematics, this problem is a NP Hard problem and the decision problem version of the same is NP Complete.

This problem has been studied widely and can be solved in many approximation techniques like - Next-Fit (NF), Next-k-Fit (NkF), First-Fit (FF), Best-Fit (BF), Worst-Fit (WF), First Fit Decreasing (FFD) etc.

The best time complexity can be O(NlogN) for this.

The approximation algorithms return us a legal solution that may not be optimized, let's have a look at the below examples,

Given an undirected graph, the vertex cover problem is to find minimum size vertex cover, this is an optimized return type, i.e. we need to minimize the size of the vertex cover. But the approximation version can be compiled as,Given an undirected graph, the vertex cover problem is to find the vertex cover, you may notice that there is no optimization(min/max requirement).

Given a set of cities and distance between every pair of cities, the problem is to find the possible route that visits every city exactly once and returns to the starting point. Notice that we don't need to find the shortest(i.e. minimum) possible root, instead we just need to find a legal route for our salesman.

The above problems are some of the examples to deliver the idea of what an Approximation Problem looks like. There are several other problems as well which are NP-hard and NP-complete. The best thing about approximation algorithms is that, you can cross proof your approach in order to know if that's a feasible one. And the probable worst thing is, these are approximations not exact. Algorithms rule the world even if that is approximate or exact. Hence let's utilize these rules to build a better world for everyone.

*Original Article Source - https://iq.opengenus.org/approximation-algorithms-intro/*

(If you don't Know Fibonacci Numbers) Fibonacci numbers is a series of numbers created in such a way that the current term in the series is the sum of previous two terms, ofcourse the first two Terms in Fibonacci are 0,1-> and then it continues this way after 0,1,-> 1,2,3,5 or you can generalize this as ...

```
F[0]=0
F[1]=1
F[i]= F[i-1]+F[i-2].
```

Now when you think about some algo to to Sum Up N fibonacci Numbers, what you can attempt is Find ALL Fibonacci up till N numbers, and now how to do this , as you saw the pattern here that i th fibonacci is sum of (i-1)th and (i-2) th Fibonacci,so let's throw some light on it You can begin with your F[0]=0 and F[1]=1 and Find All Fibonacci Up Till Nth Fibonacci, this is just some easy Dynammic Programming stuff where we have the results for the previous two fibonacci and we create our next out of it. Here is a pesudu-code for it as well

```
int F[N];
F[0]=0;
F[1]=1;
for(int i=2;i<N;i++)
{
F[i]=F[i-1]+F[i-2];
}
```

And Once you have All Fibonacci up till Nth Fibonacci we will be planning to sum them up and this again is a simple while loop

```
int sum=0;
for(int i=0;i<N;i++)
{
sum=sum+F[i];
}
return sum;
```

Now think about Time Complexity We have two loops which are going up till N , So Asymptotically this completely Boils down to **O(N)**.
Can We do it Better ?
Yes Now comes some beautiful Math along with it , Previously we were depending upon All the N terms to Sum Up N terms of Fibonacci , This time It ain't needed Let's derive a formulae for it.

As this is the recurrence for Fibonacci, So it can be re written as this by some LHS RHS shifts. Also this holds true, , So Similarly we can write these equations going this way ans so till N-> 0 If we add up all the equations we will get this result

Now I think YOu observe that what we are trying to demystify. S[N] IS SUM OF N TERMS OF FIBONACCI AND WE HAVE A CONCRETE EXPRESSION FOR IT NOW,

We No Longer Need N elements of Fibonacci to Sum N terms of Fibonacci , ALl we need now is N+2 th term of Fibonnaci to sum up till N.

How to Do it ? Optimally ?

Hint-> Use Some Linear Algebra to find any Fibonacci Term in O(LOG(N))

[This article is contributed by Mr Karthik, thanks Mr Karthik π€]

]]>Tor is a network of virtual tunnels that allows you to improve your privacy and security on the Internet.

Privarcy is a human right. We at Tor advance human rights and defend your privacy online through free software and open networks

Tor works by sending your traffic through three random servers (also known as relays) in the Tor network. The last relay in the circuit **(the exit relay)** then sends the traffic out onto the public Internet.

The image above illustrates a user browsing to different websites over Tor. The green middle computers represent relays in the Tor network, while the three keys represent the layers of encryption between the user and each relay.

It protects you by bouncing your communications around a distributed network of relays run by volunteers all around the world, it prevents somebody watching your Internet connection from learning what sites you visit and it prevents the sites you visit from learning your physical location.

This collection of volunteer relays is called the Tor network.

The way most people use Tor is with the Tor Browser, which is based on Firefox ESR and comes bundled with privacy enhancing & anti-fingerprinting features.

Nope. Tor is NOT a VPN and furthermore it is not recommended to use Tor and VPN together.
These are fundamentally very different. In the case of VPN, you're just transferring your trust from your internet service provider (ISP) to the VPN provider.
The VPN provider can log any data while you use their network and anyone snooping on the VPN connection can also *easily gain knowledge of your activities.*
** While using Tor**, your connection is made to the website by bouncing the connection through three different relays so the website you're connecting to can only see the IP address of the 'exit' relay

The Tor Browser furthermore comes bundled with features like: a different circuit of every different domain, i.e. a new circuit for every website you're connecting to so as to mitigate cross-site tracking; 'Security Settings' with which you can also disable most of the harmful JavaScript.

*The noteworthy part is that the idea is not to hide that you're using the Tor network but to anonymize your activities on the Tor network.*

*Tor is an essential tool for privacy and anonymity on the internet. It is a tool used by millions of people all around the globe - from journalists to human rights activists to ordinary people*

Direct access to the Tor network may sometimes be blocked by your Internet Service Provider or by a government. Tor Browser includes some circumvention tools for getting around these blocks. These tools are called **pluggable transports**.

I'll talk more about this in incoming blogs so stay tuned!

[This article is contributed by our Senior Frontend Developer Miss Kulsoom , Thank you Miss Developer π€]

]]>Open Source software is the software whose source code is freely available under some licensing conditions so that we can provide some extra features to the current version, find bugs and report some issues. With the help of Open-Source software, we can read its source code and if needed we can implement its some part into our project under some licensing conditions. Open Source is not only related to software but also is more related to programming languages, projects and some well-known High leveled Design Systems.

Here is the list of Some Popular Open-Source Projects:-

Mozilla Firefox Mozilla Firefox is a customizable internet browser and free open-source software. It offers thousands of plugins that are accessible with a single click of your mouse.

LibreOffice LibreOffice is a complete office suite that offers presentations, documents, spreadsheets, and databases.

GIMP Another of the best open software source examples that are worth mentioning is the photo editing tool GIMP.

VLC Media Player VLC Media Player is one of the most popular open-source software examples that you can use for free.

Linux According to a Stack Overflow survey, 83.1% of developers claimed that Linux is the most wanted platform. Linux is one of the most user-friendly open-source software on the market. It is most commonly used on Android devices and desktops.

GNU Compiler Collection GNU Compiler Collection is a collection of compilation tools for software development in the C, C++, Ada, Fortran, and other programming languages.

Python Python is common programming and scripting language used by custom software developers.

PHP When talking about the best open-source software examples of 2021, we shouldnt miss PHP.

MySQL: MySQL is an open-source relational database management system.

Nowadays open source practices are becoming very popular, developers are outsourcing their project to the world so that his/her project gets proper shape as per their needs. There is also a scenario that every person on this planet does not know each tech-stack that exists so if he/she wants to add some extra feature to his project which they might not know then they can approach a person who knows the field and can do the required changes. Many big organizations/companies like Netflix, Linux foundation, etc are making their project open source so that they get ample amount of suggestions, improvements, and bug fixes from experienced developers.

Git is the most commonly used version control system. Git tracks the changes you make to files, so you have a record of what has been done, and you can revert to specific versions should you ever need to. Git also makes collaboration easier, allowing changes by multiple people to all to be merged into one source. Git is the main root of the version control system and many company's/organizations use it on their platform for project tracking.

GitHub is a website for hosting projects that use git.

GitLab Gitlab is an open-source, powerful, secure, efficient, feature-rich, and robust application for handling software development and operations (DevOps) lifecycle. This is possibly the number one alternative for Github, as it supports group milestones, issue tracker, configurable issue boards and group issues, moving of issues between projects, and more. It also supports time tracking, powerful branching tools and protected branches and tags, file locking, merges requests, custom notifications, project roadmaps, issues weights, confidential and related issues, burndown charts for project and group milestones.

Bitbucket Bitbucket is a powerful, fully scalable, and high-performance development platform designed for professional teams. Education users and open source projects get free Bitbucket accounts and many other features. You can easily import your GitHub repositories to Bitbucket in 6 simple steps and supports third-party integrations. It has remarkable features such as Bitbucket pipelines, code search, pull requests, flexible deployment models, diff view, smart mirroring, issue tracking, IP whitelisting, and branch permissions for safeguarding your workflow.

Beanstalk Beanstalk is a powerful, secure, high-performance, and reliable platform for managing source code repositories. Beanstalk designed to improve your development workflow using features such as code review, issue tracker, repository statistics, release notes, notifications, email digests, compare view, and a full history of commits and files, and so much more.

Launchpad Launchpad is a fully free, well-known platform for building, managing, and collaborating on software projects, built by Canonical, the makers of Ubuntu Linux. It has features such as code hosting, Ubuntu package building and hosting bug tracking, code reviews, mail listing, and specification tracking. Furthermore, Launchpad supports translations, answer tracking, and FAQs.

GitBucket GitBucket is an open-source, highly pluggable Git platform that runs on JVM (Java Virtual Machine). It comes with features such as a repository viewer, issues tracker, pull requests, documentation, and wiki, as well as a plugin system to extend its core features.

To get into the world of open source, you should know at least one programming language, knowledge in the sense you should be able to solve complex problems using the language. And the second most required thing is learning git and GitHub, there are lots of resources available on the internet regarding the same. Learning doesn't mean only watching the tutorial videos, you must have a habit of "coding-along" while learning.

Now that you have learned and implemented one programming language and git/GitHub, it's time now to look for some beginner-friendly repositories in which you can make your hands dirty by making some good contributions and applying the knowledge you have gained. There are two ways in which you can find repositories.

- Using the GitHub search feature.

Here you can write your programming language name on the place where I have marked with red and hit enter then Github will list all the repositories that use the particular language.

2.Google it. You can use google search by just typing "Beginner friendly repositories for "[it's just a demo sentence].

Note:- In the above sections, I have just guided you for searching for a project related to your language, but for finding the relevant organization you need to hustle more as it requires a decent amount of searches in finding perfect-fit organizations related to your tech-stack/language.

After you found the matching repositories/organization related to your tech-stack/language then setup the code-base on your local machine and start understanding it. If you are finding any difficulty in setting up the code-base then you can contact the maintainer/mentor of the repository regarding the same. But before approaching any maintainer regarding the doubt, firstly you should see for the errors you are getting on the internet and if you are not able to tackle it then you can approach the maintainer.

After you get familiar with the code-base try to search for the issues that have a "good-first-issue" label on them, the meaning of the label is that even the beginner having the basic knowledge of the tech-stack can work on the issue. If you found some extra bug or wanted to add some extra feature then you can create an issue by yourself, but make sure that you check all the issues first to ensure that there is no such issue created first.

Open source is not about just coding, you can work on improving the documentation[Readme. md] also of the project. Documentation is also equally important as coding as it guides the collaborators step by step about the project and "How you can contribute to it".

If you get stuck on solving some issues, you can take help from other developers using the IRC channel/slack of the organization. The biggest advantage of open source is that the community is there to help you at each stage of your development.

- Always raise an issue first before creating a PR directly.
- Always make your own branch for making changes and then create a PR from your branch.
- Always be concise about your doubts.
- Be humble while asking your doubt and be patient till your doubts are resolved.
- Always follow the code of conduct and contributing guidelines of the project.
- If you are creating an issue then be descriptive about the issue, add some images related to your fixes, etc.
- During creating a PR, provide a proper description of the changes you have made with screenshots if necessary.

- Fork the repository. open your cmd.
git clone https://github.com/YOUR-USERNAME/repository-name.git

cd repository-name. i. git remote add upstream [URL of mail repository] ii. git pull upstream

4.git checkout -b "branch_name" i. git pull upstream branch_name

5.Make your changes.

6.git add . // it adds all files

7.git commit -m "commit-message"

8.git push -u origin

9.create a pull request

10.open pull request. Wait for your PR to get reviewed and merged. Congratulations ππ₯ you wrote a line of code that is going to be used by hundreds or thousands of people, this feeling can not be expressed as it has its own importance.

Even though your PR gets merged but it's your responsibility to stay in touch with the project and the maintainers, help other peers who are facing an issue.

- Ability to work with a large code-base.
- Grow your network.
- Habit of writing efficient and precise code.
- You can add your contributions to your resume.
- Improves problem-solving skills.
- Get to learn something new.

[This article has been contributed by Mr Aniket, Thanks Mr. Aniket π€]

]]>Just like the name, it means the same as sorting pancakes on a plate with a spatula, where you can only flip some of the top pancakes in the plate using spatula.

Given an unsorted array, sort the given array. You are allowed to do only following operation on array:

```
flip(arr, n): Reverse array from 0 to n
```

*Our aim is to sort the sequence in as few reversals as possible i.e. shortest ways possible.*

Let given array be arr[] and size of array be i.

Start from current size equal to n and reduce current size by one while its greater than 1. Let the current size be current_size. Do the following for every current_size

Find index of the maximum element in arr[0..current_size-1]. Let the index be mid Call flip(arr, mid) Call flip(arr, current_size-1)

```
# program to sort array using pancake sort
# Reverses arr[0..n] */
def flip(arr, n):
start = 0
while start < n:
temp = arr[start]
arr[start] = arr[n]
arr[n] = temp
start += 1
n -= 1
# to return index of the maximum
def findMax(arr, i):
mid = 0
for i in range(0,i):
if arr[n] > arr[mid]:
mid = i
return mid
#Main function
def pancakeSort(arr, i):
# Starting from the main array
current_size = i
while current_size > 1:
# Find index of the maximum element
mid = findMax(arr, current_size)
#then current size is reduced one by one
# maximum element is moved to the end
if mid != current_size-1:
# To move at the end, we first move maximum number to the beginning
flip(arr, mid)
#now we reverse the array and maximum element is moved to the end
flip(arr, current_size-1)
current_size -= 1
# Function to print an array of size i
def printArray(arr, i):
for i in range(0,i):
print ("%d"%( arr[n]),end=" ")
# Driver program
arr = [8,6,3,2,5,9,1,4,7,10]
i = len(arr)
pancakeSort(arr, i);
print ("The Sorted Array is")
printArray(arr,i)
```

Output: The Sorted Array is: 1 2 3 4 5 6 7 8 9 10

Time Complexity: **O(nlogn)**

Pancake sorting appears in applications in parallel processor networks, it provides an effective routing algorithm between processors.

It is used when the only allowed operation to sort a sequence is reversing.

[This article is contributed by Miss Aditi, Thanks Miss Aditi π€]

]]>Contractions are words or combinations of words that are shortened by dropping letters and replacing them by an apostrophe.

Nowadays, where everything is shifting online, we communicate with others more through text messages or posts on different social media like Facebook, Instagram, Whatsapp, Twitter, LinkedIn, etc. in the form of texts. With so many people to talk to, we rely on abbreviations and shortened forms of words for texting people.

For example, `Ill be there within 5 min. Are u not gng there? Am I mssng out on smthng? Id like to see u near d park.`

In English contractions, we often drop the vowels from a word to form the contractions. Removing contractions contributes to text standardization and is useful when we are working on Twitter data, on reviews of a product as the words play an important role in sentiment analysis.

####1. Using contractions library

First, install the library. You can try this library on Google colab as installing the library becomes super smooth.

Using pip:
`!pip install contractions`

In Jupyter notebook:
```
import sys
!{sys.executable} -m pip install contractions
```

*Code 1: For expanding contractions using contractions library*

Python3

```
# import library
import contractions
# contracted text
text = '''I'll be there within 5 min. Shouldn't you be there too?
I'd love to see u there my dear. It's awesome to meet new friends.
We've been waiting for this day for so long.'''
# creating an empty list
expanded_words = []
for word in text.split():
# using contractions.fix to expand the shotened words
expanded_words.append(contractions.fix(word))
expanded_text = ' '.join(expanded_words)
print('Original text: ' + text)
print('Expanded_text: ' + expanded_text)
```

Output:
```
Original text: I'll be there within 5 min. Shouldn't you be there too?
I'd love to see u there my dear. It's awesome to meet new friends.
We've been waiting for this day for so long.
```

```
Expanded_text: I will be there within 5 min. should not you be there too?
I would love to see you there my dear. it is awesome to meet new friends.
we have been waiting for this day for so long.
Removing contractions before forming word vectors helps in dimensionality reduction.
```

*Code 2: Simply using contractions.fix to expand the text.*

Python3

```
text = '''She'd like to know how I'd done that!
She's going to the park and I don't think I'll be home for dinner.
They're going to the zoo and she'll be home for dinner.'''
contractions.fix(text)
```

Output:
```
'she would like to know how I would do that!
she is going to the park and I do not think I will be home for dinner.
they are going to the zoo and she will be home for dinner.'
```

Contractions can also be handled using other techniques like dictionary mapping, and also using pycontractions library.

You can refer to the documentation of pycontractions library for learning more about this: https://pypi.org/project/pycontractions/

]]>Each node contains two fields:
**Data**: That stored at a particular address.
**Pointer**: Which contains the address of the next node.

Generally, we call the first node as **Head node** and the last node as **Tail node** in Linked List.

**There are three main linked list operations**
->Insertion
->Deletion
->Traversing

**Why Linked List Over Array?**
Array contains the following limitations:

->The size of an array is fixed. We must know the size of the array at the time of its creation subsequently, it is difficult to change its size at runtime. ->Inserting a new element in an array of elements is difficult because we need to shift elements to create room for new elements to insert. ->Deleting an element in an array is also difficult as it also takes shifting of elements in the array.

**Advantages of Linked List:**
->Insertion and deletion operations can be implemented very easily and these are not costly as they do not require shifting of elements.
->They are dynamic in nature. Hence, we can change their size whenever required.

**Types Of Linked List**
->Singly Linked List
->Circular Linked List
->Doubly Linked List

**Singly Linked List:**
A singly linked list is a type of linked list that is unidirectional, that is, it can be traversed in only one direction from head to the last node (tail).

**Circular Linked List:**
Circular Linked List is similar to singly Linked List but the last node of List has a pointer to the node which points to the head node (first node) of Linked List. We traverse a circular singly linked list until we reach the same node where we started. The circular singly liked list has no beginning and no ending.

There is no null value present in the next part of any of the nodes.

**Doubly Linked List:**
In Doubly Linked List each node apart from storing its data has two links. The first link points to the previous node in the list and the second link points to the next node in the list.

The first node of the list has its previous link pointing to NULL similarly the last node of the list has its next node pointing to NULL.

The two links help us to traverse the list in both backward and forward direction.

[This article has been contributed by Miss Tanu,Thank you Miss Tanu]

]]>##What is a GPG or GnuPG? GnuPG, is a tool that is made in accordance with the OpenPGP Internet standard . It is used primarily for secure communication and data storage. It is a tool that adds encryption and signs data.

Package repositories are downloaded round the world. Digital signatures are used to ensure that you get an exact copy from the original packager and not a malicious version from a compromised site. OpenPGP programs such as GPG are used to sign and verify those packages ensuring the authenticity of the source.

There is local disk encryption which protects data at rest. However, if you wish that only the intended recipient can open a file you send by email or drop into a shared folder, you need to encrypt that individual file. OpenPGP products like GPG can encrypt a file with a shared symmetric key or with asymmetric key pairs.

##Using GnuPG
GnuPG comes installed with most Linux distributions.
To check your current version
`$ yum list gnupg*`

The first time you run any *gpg* command, a configuration directory and keyring will be created in your home directory. For example, if you run

`gpg --list-keys`

to display keys you may see the following message:

```
$ gpg --list-keys
gpg: directory '/home/bestuser/.gnupg' created
gpg: keybox '/home/bestuser/.gnupg/pubring.kbx' created
gpg: /home/bestuser/.gnupg/trustdb.gpg: trustdb created
```

##Encrypting a file with a shared secret

You're all good to encrypt a file if the GnuPG is installed. For symmetric encryption, use the `-c`

or `--symmetric`

option and pass the file you want to encrypt. Here I'm encrypting example.txt.

```
$ gpg -c example.txt
```

The encrypted file will have a `gpg`

extension i.e `example.txt.gpg`

This encrypted file can now be sent to a remote location.

##Decryption
To decrypt the file, use the `-d`

or `--decrypt`

option.

```
$ gpg -d sample1.txt.gpg
```

Determining how to share the passphrase to decrypt it is a separate issue.

```
# names list
names = ['adi', 'prerna', 'ram']
# 'shyaam' is appended to the names list
names.append('shyaam')
# Updated namess list
print('Updated names list: ',names)
```

Output: ['adi', 'prerna', 'ram', 'shyaam']

**Python List extend()**- The extend() method adds all the elements of an iterable (list, tuple, string etc.) to the end of the list. The syntax of the extend() method is: list1.extend(iterable).

```
languages = ['Hindi', 'English']
# another list of language
languages1 = ['Bengali', 'Tamil']
# appending language1 elements to language
languages.extend(languages1)
print('Languages List:', languages)
```

Output: Languages List: ['Hindi', 'English', 'Bengali', 'Tamil']

**Python List insert()**- The list insert() method inserts an element to the list at the specified index. The syntax of the insert() method is: list.insert(i, a). Here, a is inserted to the list at the ith index. All the elements after a are shifted to the right.

```
# vowel list
vowel = ['a', 'e', 'i', 'u']
# 'o' is inserted at index 3
# the position of 'o' will be 4th
vowel.insert(3, 'o')
print('Updated List:', vowel)
```

Output: Updated List: ['a', 'e', 'i', 'o', 'u']

**Python List remove()**- The remove() method removes the first matching element (which is passed as an argument) from the list. The syntax of the remove() method is: list.remove(element). The remove() method takes a single element as an argument and removes it from the list. If the element doesn't exist, it throws ValueError: list.remove(x): x not in list exception.

```
# animals list
animals = ['cat', 'dog', 'rabbit', 'guinea pig']
# 'rabbit' is removed
animals.remove('rabbit')
# Updated animals List
print('Updated animals list: ', animals)
```

Output:Updated animals list: ['cat', 'dog', 'guinea pig']

**Python List pop()**- The pop() method removes the item at the given index from the list and returns the removed item. The syntax of the pop() method is: list.pop(index). The method takes a single argument.

```
list1 = [ 1, 2, 3, 4, 5, 6 ]
# Pops and removes the last element from the list
print(list1.pop())
# Print list after removing last element
print("New List after pop : ", list1, "\n")
list2 = [1, 2, 3, ('cat', 'bat'), 4]
# Pop last two element
print(list2.pop())
print(list2.pop())
print(list2.pop())
# Print list
print("New List after pop : ", li)
```

Output: 6 New List after pop : [1, 2, 3, 4, 5] 4 ('cat', 'bat') 3 New List after pop : [1, 2]

**Python List reverse()**- The reverse() method reverses the elements of the list. The syntax of the reverse() method is: list.reverse(). It doesn't return any value, it just updates the existing list.

```
# fruits List
fruits = ['apple', 'banana', 'orange']
print('Original List:', fruits)
# List Reverse
fruits.reverse()
# updated list
print('Updated List:', fruits)
```

Output: Original List: ['apple', 'banana', 'orange'] Updated list: ['orange', 'banana', 'apple']

**Python List Clear()**- The clear() method removes all items from the list. The syntax of clear() method is: list.clear().

```
# Defining a list
list = [{1, 2}, ('a'), ['1.1', '2.2']]
# clearing the list
list.clear()
print('List:', list)
```

Output: List: []

**Python List sort()-** The sort() method sorts the elements of a given list in a specific ascending or descending order. The syntax of the sort() method is: list.sort(key=..., reverse=...). Alternatively, you can also use Python's built-in sorted() function for the same purpose. The syntax is: sorted(list, key=..., reverse=...).

```
# vowels list
vowels = ['e', 'a', 'u', 'o', 'i']
# sort the vowels
vowels.sort()
# print vowels
print('Sorted list:', vowels)
```

Output: Sorted list: ['a', 'e', 'i', 'o', 'u']

The simplest difference between sort() and sorted() is: sort() changes the list directly and doesn't return any value, while sorted() doesn't change the list and returns the sorted list.

##**Built-In functions of the Lists**

**len()**- returns the length of the list. Syntax: list.len()**sum()**- returns the sum of the numbers in the list. Syntax: list.sum()**insert()**- Inserts object obj into list at offset index. Syntax: list.insert(index, obj)**count()**-Returns count of how many times obj occurs in list. Syntax: list.count(obj)**max()**- Returns item from the list with max value. Syntax: list.max()**min()**- Returns item from the list with min value. Syntax: list.min()

Lists in python is a very important concept which plays an important role while learning the basics of python programming. Python programming language has many out of the box features, with the applications and implementations it has become one of the most popular programming language nowadays.

Find the part-1 of this blog **here**.

[This article is contributed by Miss Aditi, Thanks Miss Aditi π€]

]]>**Data Types in Python**
Python has **six** standard Data Types. They are:

- Numbers
- String
- List
- Tuple
- Set
- Dictionary

**Lists in Python**
A list is a collection of data within square brackets and with comma- separated values. A list is mutable, which means we can modify the list.
The best thing about a list is that items in a list need not be of the same type.

Ex: [1, "abc", 98, 20.6]

A List which does not contain any values within the square brackets is known as *Empty List*.

**Properties of Lists**

*Lists are ordered*: This means that the items have a defined order, and the order will not change. If we want to add any new element to the list, those elements will be added to the end of the list.*Lists can contain any arbitrary objects*: A list can contain any amount of elements from 0 to the memory capacity of the device. The elements can be of various data types right from integer to string, float, etc.*List elements can be accessed by index*: If you want to access any element of your list, you must write your list name followed by the index number in square brackets. Remember, Python is a zero indexed language, so indexing will start from Zero. Also, a negative list index counts from the end of the list.*Lists are mutable*: A list is mutable, which means we can modify the list.*Lists Can Be Nested*: A list can contain sublists, which in turn can contain sublists themselves, and so on to arbitrary depth. 6.*Lists Allow Duplicate Values*: Since lists are indexed, lists can have items with the same value.

**How to create a list?**
Lists are created using square brackets. Whatever elements we want to create a list with, we just put those elements in square brackets separated with commas.

```
#Create a list with elements 1, 2, Sea, 23.8.
my_list= [1, 2, "Sea", 23.8]
```

Output: 1, 2, Sea, 23.8

**Creating a list with multiple distinct or duplicate elements**
A list may contain duplicate values with their distinct positions and hence, multiple distinct or duplicate values can be passed as a sequence at the time of list creation.

```
list1= [1, 2, 4, 4, 3, 3, 3, 6, 5]
print(list1)
```

Output: 1,2,4,4,3,3,3,6,5

**Accessing the elements of a list**
There's no use of creating a list if we can't access those elements. Simplest way to access an element in a list is to write the list name and then the position of the element.

```
list1= [1, 2, 3, 4, 5, 6, 7];
print(list1[3])
```

Output: 4

**Negative Indexing**
We can also do negative indexing lists. The index of -1 refers to the last item, -2 to the second last item and so on.Negative indices are counted from the end of the list.

```
list1= [1, 2, 3, 4, 5, 6, 7];
print(list1[-1])
```

Output: 7

**Accessing multiple elements of a list**
We can access a range of items in a list by using the slicing operator : (colon). So if we want to access a range, we need two indices that will slice that portion from the list.
If we want to access a range, we need two indices that will slice that portion from the list.

```
b = "Hello, World!"
print(b[-5:-2])
```

Output: orl

For the Operation and Methods of lists, please refer the **part-2** of this Blog.

[This article is contributed by Miss Aditi, Thanks Miss Aditi π€]

]]>Ok, So lets dive into the programming part. Our first aim is to create a Pandas dataframe in Python, as you may know, pandas is one of the most used libraries of Python.

**Example:**

```
# importing the pandas library
import pandas as pd
# creating a dataframe object
student_register = pd.DataFrame()
# assigning values to the
# rows and columns of the
# dataframe
student_register['Name'] = ['Abhijit',
'Smriti',
'Akash',
'Roshni']
student_register['Age'] = [20, 19, 20, 14]
student_register['Student'] = [False, True,
True, False]
student_register
```

As you can see, the dataframe object has four rows [0, 1, 2, 3] and three columns[Name, Age, Student] respectively. The column which contains the index values i.e. [0, 1, 2, 3] is known as the index column, which is a default part in pandas datagram. We can change that as per our requirement too because Python is powerful. Next, for some reason we want to add a new student in the datagram, i.e you want to add a new row to your existing data frame, that can be achieved by the following code snippet.

One important concept is that the dataframe object of Python, consists of rows which are series objects instead, stack together to form a table. Hence adding a new row means creating a new series object and appending it to the dataframe.

**Example:**

```
# creating a new pandas
# series object
new_person = pd.Series(['Mansi', 19, True],
index = ['Name', 'Age',
'Student'])
# using the .append() function
# to add that row to the dataframe
student_register.append(new_person, ignore_index = True)
```

Before processing and wrangling any data you need to get the total overview of it, which includes statistical conclusions like standard deviation(std), mean and its quartile distributions. Also, you need to know the exact information of each column, i.e. what type of value it stores and how many of them are unique. There are three support functions, .shape, .info() and .describe(), which outputs the shape of the table, information on rows and columns, and statistical information of the data frame (numerical column only) respectively.

```
# for showing the dimension
# of the dataframe
print('Shape')
print(student_register.shape)
# showing info about the data
print("\n\nInfo\n")
student_register.info()
# for showing the statistical
# info of the dataframe
print("\n\nDescribe")
student_register.describe()
```

In the above example, the .shape function gives an output (4, 3) as that is the size of the created dataframe.

The description of the output given by .info() method is as follows:

**RangeIndex**describes about the index column, i.e. [0, 1, 2, 3] in our datagram. Which is the number of rows in our dataframe. As the name suggests Data columns give the total number of columns as output.**Name**,**Age**,**Student**are the name of the columns in our data,**non-null**tells us that in the corresponding column, there is no NA/ Nan/ None value exists.**object**,**int64**and**bool**are the datatypes each column have.**dtype**gives you an overview of how many data types present in the datagram, which in term simplifies the data cleaning process. Also, in high-end machine learning models, memory usage is an important term, we cant neglect that.

The description of the output given by .describe() method is as follows:

**count**is the number of rows in the dataframe.**mean**is the mean value of all the entries in the Age column.**std**is the standard deviation of the corresponding column.**min**and**max**are the minimum and maximum entry in the column respectively.- 25%, 50% and 75% are the
**First Quartiles**,**Second Quartile(Median)**and**Third Quartile**respectively, which gives us important info on the distribution of the dataset and makes it simpler to apply an ML model.

- If N is a semiprime, then change every digit at even places of N to its corresponding matched alphabet as shown below.
- If N can be written as a sum of two primes, then change every digit at odd places of N to its corresponding matched alphabet as shown below.
- If both the condition satisfy the concatenate the above two strings formed.
- If N cant satisfy the above three criteria, then print -1. Below is the list of matched character:

**Examples:**

Input: N = 61Output: 6BExplanation: Since 61 can be expressed as a sum of two primes: 61 = 2 + 59 Therefore, the resultant string after changing the character at even index is 6B.Input: N = 1011243Output: B0B1C4DExplanation: Since 1011243 is Semiprime number: 1011243 = 3 * 337081 Therefore, the resultant string after change the character at even index is B0B1C4D.

**Approach:**

- Check if the given number N is semi prime or not by using the approach discussed in this article. If yes, then do the following:
- Convert the given number N to string(say str) using to_string() function.
- Traverse the above string formed and changed the characters at even index as:
`str[i] = char((str[i] - '0') + 65)`

- Print the new string formed.
- Check if the given number N can be expressed as a sum of two prime numbers or not using the approach discussed in this article. If yes, then do the following:
- Convert the given number N to string(say str) using to_string() function.
- Traverse the above string formed and changed the characters at odd index as:
`str[i] = char((str[i] - '0') + 65)`

- Print the new string formed.
- If the above two condition doesnt satisfy then we cant form Cypher String. Print -1.

Below is the implementation of the above approach:

```
// C++ program for the above approach
#include "bits/stdc++.h"
using namespace std;
// Function to check whether a number
// is prime or not
bool isPrime(int n)
{
if (n <= 1)
return false;
for (int i = 2; i <= sqrt(n); i++) {
if (n % i == 0)
return false;
}
return true;
}
// Function to check if a prime number
// can be expressed as sum of
// two Prime Numbers
bool isPossibleSum(int N)
{
// If the N && (N-2) is Prime
if (isPrime(N)
&& isPrime(N - 2)) {
return true;
}
else {
return false;
}
}
// Function to check semiPrime
bool checkSemiprime(int num)
{
int cnt = 0;
// Loop from 2 to sqrt(num)
for (int i = 2; cnt < 2
&& i * i <= num;
++i) {
while (num % i == 0) {
num /= i,
// Increment the count of
// prime numbers
++cnt;
}
}
// If num is greater than 1, then
// add 1 to it
if (num > 1) {
++cnt;
}
// Return '1' if count is 2 else
// return '0'
return cnt == 2;
}
// Function to make the Cypher string
void makeCypherString(int N)
{
// Resultant string
string semiPrime = "";
string sumOfPrime = "";
// Make string for the number N
string str = to_string(N);
// Check for semiPrime
if (checkSemiprime(N)) {
// Traverse to make Cypher string
for (int i = 0; str[i]; i++) {
// If index is odd add the
// current character
if (i & 1) {
semiPrime += str[i];
}
// Else current character is
// changed
else {
semiPrime
+= char(
str[i] - '0' + 65);
}
}
}
// Check for sum of two primes
if (isPossibleSum(N)) {
// Traverse to make Cypher string
for (int i = 0; str[i]; i++) {
// If index is odd then
// current character is
// changed
if (i & 1) {
sumOfPrime
+= char(
str[i] - '0' + 65);
}
// Else add the current
// character
else {
sumOfPrime += str[i];
}
}
}
// If the resultant string is ""
// then print -1
if (semiPrime + sumOfPrime == "") {
cout << "-1";
}
// Else print the resultant string
else {
cout << semiPrime + sumOfPrime;
}
}
// Driver Code
int main()
{
// Given Number
int N = 1011243;
// Function Call
makeCypherString(N);
return 0;
}
```

**Output:**

```
B0B1C4D
```

]]>**Example:**

Input: str1 = GeekforGeeks, str2 = Geeks123, N = 4Output: KiiojsvKiiowKeikw163Explanation: Caesar Text for string str1 with a shift of 4 is KiiojsvKiiow Caesar Text for string str2 with a shift of 4 at all even indexes is Keikw163 Resultant string is KiiojsvKiiow + Keikw163 = KiiojsvKiiowKeikw163

Input: str1 = ABcdE23, str2 = efda2w, N = 9Output: JKlmN12nfma1wExplanation: Caesar Text for string str1 with a shift of 9 is JKlmN12 Caesar Text for string str2 with a shift of 9 at all even indexes is nfma1w Resultant string is JKlmN12 + nfma1w = JKlmN12nfma1w

**Approach**:
This problem is an application of Caesar Cipher in Cryptography. Below are the steps:
The idea is to traverse the given string str1 and str2 and convert all the characters at every index of str1 and at even indexes of str2 by a shift of N on the basis of below 3 cases:

**Case 1**: If characters lies between A and Z then the current character is encrypted as:

```
new_character = ( (current_character - 65 + N) % 26 ) + 65;
```

**Case 2**: If characters lies between a and z then the current character is encrypted as:

```
new_character = ( (current_character - 97 + N) % 26 ) + 97;
```

**Case 3**: If characters lies between A and Z then the current character is encrypted as:

```
new_character = ( (current_character - 48 + N) % 10 ) + 48;
```

Below is the implementation of the above approach:

```
// C++ implementation of the above
// approach
#include <bits/stdc++.h>
using namespace std;
void printCaesarText(string str1,
string str2, int N)
{
// Traverse the string str1
for (int i = 0; str1[i]; i++) {
// Current character
char ch = str1[i];
// Case 1:
if (ch >= 'A' && ch <= 'Z') {
str1[i] = (ch - 65 + N) % 26 + 65;
}
// Case 2:
else if (ch >= 'a' && ch <= 'z') {
str1[i] = (ch - 97 + N) % 26 + 97;
}
// Case 3:
else if (ch >= '0' && ch <= '9') {
str1[i] = (ch - 48 + N) % 10 + 48;
}
}
for (int i = 0; str2[i]; i++) {
// If current index is odd, then
// do nothing
if (i & 1)
continue;
// Current character
char ch = str2[i];
// Case 1:
if (ch >= 'A' && ch <= 'Z') {
str2[i] = (ch - 65 + N) % 26 + 65;
}
// Case 2:
else if (ch >= 'a' && ch <= 'z') {
str2[i] = (ch - 97 + N) % 26 + 97;
}
// Case 3:
else if (ch >= '0' && ch <= '9') {
str2[i] = (ch - 48 + N) % 10 + 48;
}
}
// Print the concatenated strings
// str1 + str2
cout << str1 + str2;
}
// Driver Code
int main()
{
string str1 = "GeekforGeeks";
string str2 = "Geeks123";
int N = 4;
printCaesarText(str1, str2, N);
return 0;
}
```

Output:

```
KiiojsvKiiowKeikw163
```

**Time Complexity**: O(N + M), where N and M are the lengths of the two given string.

Threading in python is used to run multiple threads (tasks, function calls) at the same time. This means that your program will have two things happening at once. Threading is a feature usually provided by the operating system. Each thread has its own memory space.

```
#Program for time estimation of a function without threads.
#import time module
import time
#initializing start variable with time in seconds before the code execution
start = time.perf_counter()
def do_something ():
print ('Sleeping 1 second')
time.sleep(1)
print ('Done sleeping')
#If we call the do_something() once it will take 1.01 seconds for execution.
do_something()
#But if call the do_something() twice, it will take upto 2.02 seconds for exection.
#The program runs synchronously.
do_something()
finish = time.perf_counter ();
print ('Finished in' + str(round(finish-start,2)) + ' seconds')
```

```
#Program for time estimation of a function with threads.
#import module
import time
import threading
start = time.perf_counter()
def do_something ():
print('Sleeping 1 second....')
time.sleep(1)
print ('Done sleeping...')
#Assigning thread to each function for parallel execution.
t1 = threading.Thread(target = do_something)
t2 = threading.Thread(target = do_something)
#Telling our OS to execute the threads
t1.start()
t2.start()
t1.join()
t2.join()
finish = time.perf_counter()
#Because of threads process gets completed in 1.02 seconds
print ('Finished in' + str(round(finish-start,2)) + ' seconds')
```

In threading, the operating system actually knows about each thread and can interrupt it at any time to start running a different thread. Threading runs on a single processor and therefore only runs one at a time.

The dictionary definition of concurrency is simultaneous occurrence. Concurrency is great for IO-bound operations such as for reading a file or communicating over a network. If many threads start executing simultaneously then we can say that it is concurrent.

In multithreading, the concept of threads is used. Multithreading is defined as the ability of a processor to execute multiple threads concurrently.

The Python Global Interpreter Lock or GIL, in simple words, is a mutex (or a lock) that allows only one thread to hold the control of the Python interpreter. This means that only one thread can be in a state of execution at any point in time. The impact of the GIL isnt visible to developers who execute single-threaded programs. This means that in python only one thread will be executed at a time. The performance of the single-threaded process and the multi-threaded process will be the same in python and this is because of GIL in python. Python has a reference count variable that keeps track of the number of references that point to the object. When this count reaches zero, the memory occupied by the object is released.

[This article has been contributed by Mr Aniket, Thanks Mr. Aniket π€]

]]>We are an organization providing a lot of services like data analytics, freelance designing, writing and a few products based on education & software development like our magazine "The Developer Quest" and our courses. Other than this, we are deeply involved in promoting opensource development by preparing different projects and helping beginners by participating in many national and international programs.

With the evolvement of new technology and in the fast-paced age of edu-tech it's been quite easy to learn a new skill, perform some hands-on, prepare some projects and master the same skill completely. Our main vision is to provide quality content to our learners, which focuses more on learning than business. In India, we have seen the education-tech industry to be growing at a higher pace, but that's becoming too much costly to buy a course for only a 5 to 6 months access. We don't believe in courses, rather we believe in hands-on application. This is one of the reasons we are very much focused on opensource & digital e-books/magazine.

Check our digital magazine " The Developer Quest " here.

Python is easy to use, powerful, and versatile, making it a great choice for beginners and experts alike. Pythons readability makes it a great first programming language, it allows you to think like a programmer and not waste time with confusing syntax.

Open-source licensing encourages innovation through collaboration. Without it, many of the technologies we take for granted today would never have developed or would be locked away behind patent law. The open-source movement is the reason that technology has developed at such a breakneck pace for the past few decades.

We are very fond of developers out there, we want them to contribute and grow along with us. The contributions that our developers have made previously are precious and we are glad to have them here.

To contribute to our projects get in touch at - opensource@edualgoacademy.com

]]>Yield is a keyword in python which is used to throw[return] a single value from a list at a time to a calling function, whenever a function encounters a yield keyword it pauses the execution of the function and throws specified values by maintaining the local memory.

```
# Function which yields the numbers.
def normal_yield ():
yield 1
yield 2
yield 3
# iterate over the numbers
for value in normal_yield() :
print (value)
```

Whenever calling function execution stops, then control passes to the next line below the yield keyword in the called function.

```
# Program to count numbers 1 to 10.
def number_count ():
# value initialization.
i = 0
# infinite loop.
while True:
yield i
# after function called, flow execution starts from here.
i = i + 1
# Priting the values.
for count in number_count ():
if count > 10 :
break
print (count)
```

We can also iterate through the list using the next() iterator function after the list is yielded. Although 'for in' loop is much more convenient than the next() method in case of execution time.

```
def square_number (nums):
for i in nums:
yield (i*i)
#variable holding the yielded number
my_nums = square_number ([1,2,3,4,5])
#iterating using the next() functions
print (next(my_nums))
print (next(my_nums))
print (next(my_nums))
print (next(my_nums))
print (next(my_nums))
```

Generators are like the normal python functions which we define, but if the function contains the 'yield' keyword in the definition then the function becomes a generator function. Yield is a keyword that makes the generator function.

```
def compute ():
i=0
while True:
yield i
i = i + 1
for check in compute ():
if check % 2 == 0:
print(str(check) + " is Even")
elif check > 10:
break
```

Whenever a function runs, the return statement has to wait for the process to complete and after the function execution comes to an end it returns the whole variable or list to the calling function. This takes some time for execution as well as occupies the memory for the whole list. The next time we want to execute the same function then it will run from the beginning.

```
def square_numbers (nums):
result = []
for i in nums:
result.append (i*i)
#return has to wait to process the above statements.
return result
my_number = square_numbers ([1,2,3,4,5])
print (my_number)
```

If the function encounters yield keyword it pauses the execution and sends the value to the calling function with keeping local memory as it is, after the calling function executes its task the process returns to the next line of yield in the function definition. With this, the execution speed is maintained as well as it also takes very little space in memory.

```
def number_square (nums):
for i in nums:
yield (i*i)
numbers = number_square ([1,2,3,4,5])
for value in numbers:
print (value)
```

- Works in a better way with large sets of data as generators throw only one value at a time instead of returning the whole list like a return.
- Return statement consumes lots of space in memory and also takes more time for execution while generators don't.
- Return allows the function to execute from the beginning while yield only executes the statement below it and maintains local memory for variables.

- The only disadvantage of generators is that they are not commonly used and are less popular.

[This article has been contributed by Mr Aniket, Thanks Mr. Aniket π€]

]]>